diff options
Diffstat (limited to 'src/gui/text')
32 files changed, 340 insertions, 171 deletions
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp index 8528f59844..7aa3aa7453 100644 --- a/src/gui/text/qabstracttextdocumentlayout.cpp +++ b/src/gui/text/qabstracttextdocumentlayout.cpp @@ -436,7 +436,8 @@ void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *compo if (!iface) return; // ### print error message on terminal? - connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*))); + QObjectPrivate::connect(component, &QObject::destroyed, d, + &QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed); QTextObjectHandler h; h.iface = iface; @@ -457,7 +458,8 @@ void QAbstractTextDocumentLayout::unregisterHandler(int objectType, QObject *com const auto it = d->handlers.constFind(objectType); if (it != d->handlers.cend() && (!component || component == it->component)) { if (component) - disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*))); + QObjectPrivate::disconnect(component, &QObject::destroyed, d, + &QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed); d->handlers.erase(it); } } diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h index 397dcd37d4..1e97ba3785 100644 --- a/src/gui/text/qabstracttextdocumentlayout.h +++ b/src/gui/text/qabstracttextdocumentlayout.h @@ -129,7 +129,6 @@ private: friend class QTextEngine; friend class QTextLayout; friend class QTextLine; - Q_PRIVATE_SLOT(d_func(), void _q_handlerDestroyed(QObject *obj)) Q_PRIVATE_SLOT(d_func(), int _q_dynamicPageCountSlot()) Q_PRIVATE_SLOT(d_func(), QSizeF _q_dynamicDocumentSizeSlot()) }; diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 663f727145..bcdeafdbbd 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -1140,14 +1140,14 @@ static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAd s.chop(2); value.variant = s; if (value.variant.convert((QVariant::Type)qMetaTypeId<qreal>())) { - font->setPointSizeF(value.variant.toReal()); + font->setPointSizeF(qBound(qreal(0), value.variant.toReal(), qreal(1 << 24) - 1)); valid = true; } } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { s.chop(2); value.variant = s; if (value.variant.convert(QMetaType::Int)) { - font->setPixelSize(value.variant.toInt()); + font->setPixelSize(qBound(0, value.variant.toInt(), (1 << 24) - 1)); valid = true; } } @@ -1192,7 +1192,7 @@ static bool setFontWeightFromValue(const QCss::Value &value, QFont *font) } if (value.type != Value::Number) return false; - font->setWeight(qMin(value.variant.toInt() / 8, 99)); + font->setWeight(qRound(qBound(0.0, value.variant.toDouble() / 8.0, 99.0))); return true; } diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 94020dc665..9f16af0222 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -220,8 +220,10 @@ QFontPrivate::~QFontPrivate() if (engineData && !engineData->ref.deref()) delete engineData; engineData = nullptr; - if (scFont && scFont != this) - scFont->ref.deref(); + if (scFont && scFont != this) { + if (!scFont->ref.deref()) + delete scFont; + } scFont = nullptr; } @@ -630,8 +632,10 @@ void QFont::detach() if (d->engineData && !d->engineData->ref.deref()) delete d->engineData; d->engineData = nullptr; - if (d->scFont && d->scFont != d.data()) - d->scFont->ref.deref(); + if (d->scFont && d->scFont != d.data()) { + if (!d->scFont->ref.deref()) + delete d->scFont; + } d->scFont = nullptr; return; } @@ -2080,7 +2084,26 @@ QString QFont::key() const /*! Returns a description of the font. The description is a comma-separated list of the attributes, perfectly suited for use - in QSettings. + in QSettings, and consists of the following: + + \list + \li Font family + \li Point size + \li Pixel size + \li Style hint + \li Font weight + \li Font style + \li Underline + \li Strike out + \li Fixed pitch + \li Always \e{0} + \li Capitalization + \li Letter spacing + \li Word spacing + \li Stretch + \li Style strategy + \li Font style (omitted when unavailable) + \endlist \sa fromString() */ @@ -3297,3 +3320,5 @@ QDebug operator<<(QDebug stream, const QFont &font) #endif QT_END_NAMESPACE + +#include "moc_qfont.cpp" diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 1a4d8f938b..2011f935a9 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -414,6 +414,25 @@ void QtFontFamily::ensurePopulated() Q_ASSERT_X(populated, Q_FUNC_INFO, qPrintable(name)); } +/*! + \internal + + Tests if the given family \a family supports writing system \a writingSystem, + including the special case for Han script mapping to several subsequent writing systems +*/ +static bool familySupportsWritingSystem(QtFontFamily *family, size_t writingSystem) +{ + Q_ASSERT(family != nullptr); + Q_ASSERT(writingSystem != QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount); + + size_t ws = writingSystem; + do { + if ((family->writingSystems[ws] & QtFontFamily::Supported) != 0) + return true; + } while (writingSystem >= QFontDatabase::SimplifiedChinese && writingSystem <= QFontDatabase::Japanese && ++ws <= QFontDatabase::Japanese); + + return false; +} struct FallbacksCacheKey { QString family; @@ -826,7 +845,7 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo f->ensurePopulated(); - if (writingSystem > QFontDatabase::Any && f->writingSystems[writingSystem] != QtFontFamily::Supported) + if (writingSystem > QFontDatabase::Any && !familySupportsWritingSystem(f, writingSystem)) continue; for (int j = 0; j < f->count; ++j) { @@ -1219,9 +1238,13 @@ static bool matchFamilyName(const QString &familyName, QtFontFamily *f) Tries to find the best match for a given request and family/foundry */ -static int match(int script, const QFontDef &request, - const QString &family_name, const QString &foundry_name, - QtFontDesc *desc, const QList<int> &blacklistedFamilies) +static int match(int script, + const QFontDef &request, + const QString &family_name, + const QString &foundry_name, + QtFontDesc *desc, + const QList<int> &blacklistedFamilies, + unsigned int *resultingScore = nullptr) { int result = -1; @@ -1271,8 +1294,10 @@ static int match(int script, const QFontDef &request, test.family->ensurePopulated(); // Check if family is supported in the script we want - if (writingSystem != QFontDatabase::Any && !(test.family->writingSystems[writingSystem] & QtFontFamily::Supported)) + if (writingSystem != QFontDatabase::Any + && !familySupportsWritingSystem(test.family, writingSystem)) { continue; + } // as we know the script is supported, we can be sure // to find a matching font here. @@ -1296,6 +1321,10 @@ static int match(int script, const QFontDef &request, if (newscore < 10) // xlfd instead of FT... just accept it break; } + + if (resultingScore != nullptr) + *resultingScore = score; + return result; } @@ -1729,13 +1758,19 @@ bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &sty for (int j = 0; j < f->count; j++) { QtFontFoundry *foundry = f->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { - for (int k = 0; k < foundry->count; k++) - if ((style.isEmpty() || - foundry->styles[k]->styleName == style || - foundry->styles[k]->key == styleKey) && foundry->styles[k]->smoothScalable) { - smoothScalable = true; + for (int k = 0; k < foundry->count; k++) { + QtFontStyle *fontStyle = foundry->styles[k]; + smoothScalable = + fontStyle->smoothScalable + && ((style.isEmpty() + || fontStyle->styleName == style + || fontStyle->key == styleKey) + || (fontStyle->styleName.isEmpty() + && style == styleStringHelper(fontStyle->key.weight, + QFont::Style(fontStyle->key.style)))); + if (smoothScalable) goto end; - } + } } } end: @@ -2474,13 +2509,12 @@ bool QFontDatabasePrivate::isApplicationFont(const QString &fileName) with removeApplicationFont() or to retrieve the list of family names contained in the font. +//! [add-application-font-doc] The function returns -1 if the font could not be loaded. Currently only TrueType fonts, TrueType font collections, and OpenType fonts are supported. - - \note Adding application fonts on Unix/X11 platforms without fontconfig is - currently not supported. +//! [add-application-font-doc] \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont() */ @@ -2508,12 +2542,7 @@ int QFontDatabase::addApplicationFont(const QString &fileName) with removeApplicationFont() or to retrieve the list of family names contained in the font. - The function returns -1 if the font could not be loaded. - - Currently only TrueType fonts and TrueType font collections are supported. - - \b{Note:} Adding application fonts on Unix/X11 platforms without fontconfig is - currently not supported. + \include qfontdatabase.cpp add-application-font-doc \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont() */ @@ -2648,7 +2677,9 @@ bool QFontDatabase::supportsThreadedFontRendering() /*! \internal */ -QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) +QFontEngine *QFontDatabase::findFont(const QFontDef &request, + int script, + bool preferScriptOverFamily) { QMutexLocker locker(fontDatabaseMutex()); @@ -2679,16 +2710,31 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) return engine; } + if (request.pixelSize > 0xffff) { + // Stop absurd requests reaching the engines; pixel size is assumed to fit ushort + qCDebug(lcFontMatch, "Rejecting request for pixel size %g2, returning box engine", double(request.pixelSize)); + return new QFontEngineBox(32); // not request.pixelSize, to avoid overflow/DOS + } + QString family_name, foundry_name; const QString requestFamily = request.families.size() > 0 ? request.families.at(0) : request.family; parseFontName(requestFamily, foundry_name, family_name); QtFontDesc desc; QList<int> blackListed; - int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); - if (index < 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) { + unsigned int score = UINT_MAX; + int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed, &score); + if (score > 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) { // We populated familiy aliases (e.g. localized families), so try again index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); } + + // If we do not find a match and NoFontMerging is set, use the requested font even if it does + // not support the script. + // + // (we do this at the end to prefer foundries that support the script if they exist) + if (index < 0 && !multi && !preferScriptOverFamily) + index = match(QChar::Script_Common, request, family_name, foundry_name, &desc, blackListed); + if (index >= 0) { QFontDef fontDef = request; @@ -2881,10 +2927,8 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script } uint order = i; - if (testFamily == nullptr - || (testFamily->writingSystems[writingSystem] & QtFontFamily::Supported) == 0) { + if (testFamily == nullptr || !familySupportsWritingSystem(testFamily, writingSystem)) order |= 1u << 31; - } supported.insert(order, family); } @@ -2894,3 +2938,5 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script QT_END_NAMESPACE +#include "moc_qfontdatabase.cpp" + diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h index 80b092f177..b581f30a19 100644 --- a/src/gui/text/qfontdatabase.h +++ b/src/gui/text/qfontdatabase.h @@ -160,7 +160,7 @@ private: static void createDatabase(); static void parseFontName(const QString &name, QString &foundry, QString &family); static QString resolveFontFamilyAlias(const QString &family); - static QFontEngine *findFont(const QFontDef &request, int script /* QChar::Script */); + static QFontEngine *findFont(const QFontDef &request, int script /* QChar::Script */, bool preferScriptOverFamily = false); static void load(const QFontPrivate *d, int script /* QChar::Script */); friend struct QFontDef; diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index f1fd755e91..d9c0239940 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1815,7 +1815,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili void QFontEngineMulti::ensureEngineAt(int at) { - if (!m_fallbackFamiliesQueried) + if (!m_fallbackFamiliesQueried && at > 0) ensureFallbackFamiliesQueried(); Q_ASSERT(at < m_engines.size()); if (!m_engines.at(at)) { diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 73fcc4bc78..72e3969427 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -273,9 +273,8 @@ bool QFontMetrics::operator ==(const QFontMetrics &other) const The ascent of a font is the distance from the baseline to the highest position characters extend to. In practice, some font designers break this rule, e.g. when they put more than one accent - on top of a character, or to accommodate an unusual character in - an exotic language, so it is possible (though rare) that this - value will be too small. + on top of a character, or to accommodate a certain character, so it + is possible (though rare) that this value will be too small. \sa descent() */ @@ -310,8 +309,8 @@ int QFontMetrics::capHeight() const The descent is the distance from the base line to the lowest point characters extend to. In practice, some font designers break this rule, - e.g. to accommodate an unusual character in an exotic language, so - it is possible (though rare) that this value will be too small. + e.g. to accommodate a certain character, so it is possible (though + rare) that this value will be too small. \sa ascent() */ @@ -1242,9 +1241,8 @@ bool QFontMetricsF::operator ==(const QFontMetricsF &other) const The ascent of a font is the distance from the baseline to the highest position characters extend to. In practice, some font designers break this rule, e.g. when they put more than one accent - on top of a character, or to accommodate an unusual character in - an exotic language, so it is possible (though rare) that this - value will be too small. + on top of a character, or to accommodate a certain character, so + it is possible (though rare) that this value will be too small. \sa descent() */ @@ -1280,8 +1278,8 @@ qreal QFontMetricsF::capHeight() const The descent is the distance from the base line to the lowest point characters extend to. (Note that this is different from X, which adds 1 pixel.) In practice, some font designers break this rule, - e.g. to accommodate an unusual character in an exotic language, so - it is possible (though rare) that this value will be too small. + e.g. to accommodate a certain character, so it is possible (though + rare) that this value will be too small. \sa ascent() */ @@ -1697,7 +1695,9 @@ QRectF QFontMetricsF::boundingRect(QChar ch) const Returns the bounding rectangle of the characters in the given \a text. This is the set of pixels the text would cover if drawn when constrained - to the bounding rectangle specified by \a rect. + to the bounding rectangle specified by \a rect. If \a rect is a reference + to a \nullptr object, e.g. when passing a default constructed QRectF, the + bounding rectangle will not constrain itself to the size. The \a flags argument is the bitwise OR of the following flags: \list diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index d56516fa04..75639ee525 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -112,24 +112,6 @@ QByteArray QFontSubset::glyphName(unsigned short unicode, bool symbol) return buffer; } -QByteArray QFontSubset::glyphName(unsigned int glyph, const QVector<int> &reverseMap) const -{ - uint glyphIndex = glyph_indices[glyph]; - - if (glyphIndex == 0) - return "/.notdef"; - - QByteArray ba; - QPdf::ByteStream s(&ba); - if (reverseMap[glyphIndex] && reverseMap[glyphIndex] < 0x10000) { - s << '/' << glyphName(reverseMap[glyphIndex], false); - } else { - s << "/gl" << (int)glyphIndex; - } - return ba; -} - - QByteArray QFontSubset::widthArray() const { Q_ASSERT(!widths.isEmpty()); diff --git a/src/gui/text/qfontsubset_p.h b/src/gui/text/qfontsubset_p.h index e7c6053beb..5b605aac59 100644 --- a/src/gui/text/qfontsubset_p.h +++ b/src/gui/text/qfontsubset_p.h @@ -77,7 +77,6 @@ public: QByteArray widthArray() const; QByteArray createToUnicodeMap() const; QVector<int> getReverseMap() const; - QByteArray glyphName(unsigned int glyph, const QVector<int> &reverseMap) const; static QByteArray glyphName(unsigned short unicode, bool symbol); diff --git a/src/gui/text/qinputcontrol.cpp b/src/gui/text/qinputcontrol.cpp index 3381fdb673..8c05346730 100644 --- a/src/gui/text/qinputcontrol.cpp +++ b/src/gui/text/qinputcontrol.cpp @@ -79,6 +79,9 @@ bool QInputControl::isAcceptableInput(const QKeyEvent *event) const if (c.category() == QChar::Other_PrivateUse) return true; + if (c.isHighSurrogate() && text.length() > 1 && text.at(1).isLowSurrogate()) + return true; + if (m_type == TextEdit && c == QLatin1Char('\t')) return true; @@ -137,3 +140,5 @@ bool QInputControl::isCommonTextEditShortcut(const QKeyEvent *ke) } QT_END_NAMESPACE + +#include "moc_qinputcontrol_p.cpp" diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index b4b60cbaaf..3d6b70a995 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -760,7 +760,7 @@ QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writ QFontDef request(multiEngine->fontDef); request.styleStrategy |= QFont::NoFontMerging; - if (QFontEngine *engine = QFontDatabase::findFont(request, script)) { + if (QFontEngine *engine = QFontDatabase::findFont(request, script, true)) { if (request.weight > QFont::Normal) engine->fontDef.weight = request.weight; if (request.style > QFont::StyleNormal) diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 7ee9898537..b75904e885 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -3032,10 +3032,12 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length()) html += QLatin1String("<!--EndFragment-->"); + QString closeTags; + if (pre) html += QLatin1String("</pre>"); else if (list) - html += QLatin1String("</li>"); + closeTags += QLatin1String("</li>"); else { int headingLevel = blockFormat.headingLevel(); if (headingLevel > 0 && headingLevel <= 6) @@ -3047,9 +3049,30 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) if (list) { if (list->itemNumber(block) == list->count() - 1) { // last item? close list if (isOrderedList(list->format().style())) - html += QLatin1String("</ol>"); + closeTags += QLatin1String("</ol>"); else - html += QLatin1String("</ul>"); + closeTags += QLatin1String("</ul>"); + } + const QTextBlock nextBlock = block.next(); + // If the next block is the beginning of a new deeper nested list, then we don't + // want to close the current list item just yet. This should be closed when this + // item is fully finished + if (nextBlock.isValid() && nextBlock.textList() && + nextBlock.textList()->itemNumber(nextBlock) == 0 && + nextBlock.textList()->format().indent() > list->format().indent()) { + QString lastTag; + if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1) + lastTag = closingTags.takeLast(); + lastTag.prepend(closeTags); + closingTags << lastTag; + } else if (list->itemNumber(block) == list->count() - 1) { + // If we are at the end of the list now then we can add in the closing tags for that + // current block + html += closeTags; + if (!closingTags.isEmpty()) + html += closingTags.takeLast(); + } else { + html += closeTags; } } @@ -3476,3 +3499,5 @@ QTextDocumentPrivate *QTextDocument::docHandle() const */ QT_END_NAMESPACE + +#include "moc_qtextdocument.cpp" diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 524931ebde..c22dd95d48 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -381,8 +381,11 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo Q_ASSERT(blocks.length() == fragments.length()); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat)); - if (group) + if (group) { group->blockInserted(QTextBlock(this, b)); + docChangeOldLength--; + docChangeLength--; + } QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format))); if (frame) { diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 7f85d27ba0..9908f14ba3 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -405,6 +405,7 @@ private: QTextCharFormat defaultCharFormat; const QTextDocument *doc; bool fragmentMarkers; + QStringList closingTags; }; QT_END_NAMESPACE diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index d7bc707491..2fdfcaa2d2 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -1296,5 +1296,6 @@ QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html, const return res; } -QT_END_NAMESPACE #endif // QT_NO_TEXTHTMLPARSER + +QT_END_NAMESPACE diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index a2b3c8dc76..72267e9380 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -105,14 +105,13 @@ public: bool sizeDirty; bool layoutDirty; - bool fullLayoutCompleted; QVector<QPointer<QTextFrame> > floats; }; QTextFrameData::QTextFrameData() : maximumWidth(QFIXED_MAX), - currentLayoutStruct(nullptr), sizeDirty(true), layoutDirty(true), fullLayoutCompleted(false) + currentLayoutStruct(nullptr), sizeDirty(true), layoutDirty(true) { } @@ -2124,7 +2123,7 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p { Q_Q(const QTextDocumentLayout); const QTextBlockFormat blockFormat = bl.blockFormat(); - const QTextCharFormat charFormat = QTextCursor(bl).charFormat(); + const QTextCharFormat charFormat = bl.charFormat(); QFont font(charFormat.font()); if (q->paintDevice()) font = QFont(font, q->paintDevice()); @@ -2187,9 +2186,11 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p painter->setRenderHint(QPainter::Antialiasing); + const bool marker = bl.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker; if (selectionFormat) { painter->setPen(QPen(selectionFormat->foreground(), 0)); - painter->fillRect(r, selectionFormat->background()); + if (!marker) + painter->fillRect(r, selectionFormat->background()); } else { QBrush fg = charFormat.foreground(); if (fg == Qt::NoBrush) @@ -2199,19 +2200,21 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p QBrush brush = context.palette.brush(QPalette::Text); - bool marker = bl.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker; if (marker) { int adj = fontMetrics.lineSpacing() / 6; r.adjust(-adj, 0, -adj, 0); + const QRectF outer = r.adjusted(-adj, -adj, adj, adj); + if (selectionFormat) + painter->fillRect(outer, selectionFormat->background()); if (bl.blockFormat().marker() == QTextBlockFormat::MarkerType::Checked) { - // ### Qt6: render with QStyle / PE_IndicatorCheckBox. We don't currently + // ### Qt7: render with QStyle / PE_IndicatorCheckBox. We don't currently // have access to that here, because it would be a widget dependency. painter->setPen(QPen(painter->pen().color(), 2)); painter->drawLine(r.topLeft(), r.bottomRight()); painter->drawLine(r.topRight(), r.bottomLeft()); painter->setPen(QPen(painter->pen().color(), 0)); } - painter->drawRect(r.adjusted(-adj, -adj, adj, adj)); + painter->drawRect(outer); } switch (style) { @@ -2358,9 +2361,10 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT floatMinWidth = qMax(floatMinWidth, cd->minimumWidth); } - // constraint the maximumWidth by the minimum width of the fixed size floats, to - // keep them visible + // constraint the maximum/minimumWidth by the minimum width of the fixed size floats, + // to keep them visible layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth); + layoutStruct.minimumWidth = qMax(layoutStruct.minimumWidth, floatMinWidth); // as floats in cells get added to the table's float list but must not affect // floats in other cells we must clear the list here. @@ -2537,6 +2541,8 @@ recalc_minmax_widths: for (int n = 0; n < cspan; ++n) { const int col = i + n; QFixed w = widthToDistribute / (cspan - n); + if (td->maxWidths[col] != QFIXED_MAX) + w = qMax(td->maxWidths[col], w); td->maxWidths[col] = qMax(td->minWidths.at(col), w); widthToDistribute -= td->maxWidths.at(col); if (widthToDistribute <= 0) @@ -2944,7 +2950,7 @@ QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, in QTextFrameData *fd = data(f); QFixed newContentsWidth; - bool fullLayout = (f == document->rootFrame() && !fd->fullLayoutCompleted); + bool fullLayout = false; { QTextFrameFormat fformat = f->frameFormat(); // set sizes of this frame from the format @@ -3398,7 +3404,6 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout cp.contentsWidth = layoutStruct->contentsWidth; checkPoints.append(cp); checkPoints.reserve(checkPoints.size()); - fd->fullLayoutCompleted = true; } else { currentLazyLayoutPosition = checkPoints.constLast().positionInFrame; // ####### @@ -3810,7 +3815,6 @@ void QTextDocumentLayout::documentChanged(int from, int oldLength, int length) d->contentHasAlignment = false; d->currentLazyLayoutPosition = 0; d->checkPoints.clear(); - data(d->docPrivate->rootFrame())->fullLayoutCompleted = false; d->layoutStep(); } else { d->ensureLayoutedByPosition(from); diff --git a/src/gui/text/qtextdocumentwriter.cpp b/src/gui/text/qtextdocumentwriter.cpp index 0bafa5d9ff..800b72ea9c 100644 --- a/src/gui/text/qtextdocumentwriter.cpp +++ b/src/gui/text/qtextdocumentwriter.cpp @@ -83,7 +83,6 @@ public: \inmodule QtGui \ingroup richtext-processing - \ingroup io To write a document, construct a QTextDocumentWriter object with either a file name or a device object, and specify the document format to be @@ -249,9 +248,11 @@ QString QTextDocumentWriter::fileName () const */ bool QTextDocumentWriter::write(const QTextDocument *document) { - QByteArray suffix; + if (!d->device) + return false; - if (d->device && d->format.isEmpty()) { + QByteArray suffix; + if (d->format.isEmpty()) { // if there's no format, see if device is a file, and if so, find // the file suffix if (QFile *file = qobject_cast<QFile *>(d->device)) diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index b7459bf826..6336fadf74 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -258,7 +258,7 @@ struct QBidiAlgorithm { for (int i = 0; i < length; ++i) { int pos = i; uint uc = text[i].unicode(); - if (QChar::isHighSurrogate(uc) && i < length - 1) { + if (QChar::isHighSurrogate(uc) && i < length - 1 && text[i + 1].isLowSurrogate()) { ++i; analysis[i].bidiDirection = QChar::DirNSM; uc = QChar::surrogateToUcs4(ushort(uc), text[i].unicode()); @@ -1373,9 +1373,15 @@ static void applyVisibilityRules(ushort ucs, QGlyphLayout *glyphs, uint glyphPos if (!fontEngine->symbol) { // U+00AD [SOFT HYPHEN] is a default ignorable codepoint, // so we replace its glyph and metrics with ones for - // U+002D [HYPHEN-MINUS] and make it visible if it appears at line-break + // U+002D [HYPHEN-MINUS] or U+2010 [HYPHEN] and make + // it visible if it appears at line-break const uint engineIndex = glyphs->glyphs[glyphPosition] & 0xff000000; - glyphs->glyphs[glyphPosition] = fontEngine->glyphIndex('-'); + glyph_t glyph = fontEngine->glyphIndex(0x002d); + if (glyph == 0) + glyph = fontEngine->glyphIndex(0x2010); + if (glyph == 0) + glyph = fontEngine->glyphIndex(0x00ad); + glyphs->glyphs[glyphPosition] = glyph; if (Q_LIKELY(glyphs->glyphs[glyphPosition] != 0)) { glyphs->glyphs[glyphPosition] |= engineIndex; QGlyphLayout tmp = glyphs->mid(glyphPosition, 1); @@ -1547,12 +1553,27 @@ void QTextEngine::shapeText(int item) const } else { si.num_glyphs = shapeTextWithHarfbuzz(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled); } + if (Q_UNLIKELY(si.num_glyphs == 0)) { - Q_UNREACHABLE(); // ### report shaping errors somehow + if (Q_UNLIKELY(!ensureSpace(si.glyph_data_offset + 1))) { + qWarning() << "Unable to allocate space for place-holder glyph"; + return; + } + + si.num_glyphs = 1; + + // Overwrite with 0 token to indicate failure + QGlyphLayout g = availableGlyphs(&si); + g.glyphs[0] = 0; + g.attributes[0].clusterStart = true; + + ushort *log_clusters = logClusters(&si); + for (int i = 0; i < itemLength; ++i) + log_clusters[i] = 0; + return; } - layoutData->used += si.num_glyphs; QGlyphLayout glyphs = shapedGlyphs(&si); @@ -2164,20 +2185,35 @@ void QTextEngine::itemize() const QTextDocumentPrivate::FragmentIterator end = p->find(block.position() + block.length() - 1); // -1 to omit the block separator char int format = it.value()->format; + int preeditPosition = s ? s->preeditPosition : INT_MAX; int prevPosition = 0; int position = prevPosition; while (1) { const QTextFragmentData * const frag = it.value(); if (it == end || format != frag->format) { - if (s && position >= s->preeditPosition) { + if (s && position >= preeditPosition) { position += s->preeditText.length(); - s = nullptr; + preeditPosition = INT_MAX; } Q_ASSERT(position <= length); QFont::Capitalization capitalization = formatCollection()->charFormat(format).hasProperty(QTextFormat::FontCapitalization) ? formatCollection()->charFormat(format).fontCapitalization() : formatCollection()->defaultFont().capitalization(); + if (s) { + for (const auto &range : qAsConst(s->formats)) { + if (range.start + range.length <= prevPosition || range.start >= position) + continue; + if (range.format.hasProperty(QTextFormat::FontCapitalization)) { + if (range.start > prevPosition) + itemizer.generate(prevPosition, range.start - prevPosition, capitalization); + int newStart = std::max(prevPosition, range.start); + int newEnd = std::min(position, range.start + range.length); + itemizer.generate(newStart, newEnd - newStart, range.format.fontCapitalization()); + prevPosition = newEnd; + } + } + } itemizer.generate(prevPosition, position - prevPosition, capitalization); if (it == end) { if (position < length) diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 4e5ee41e24..560a0a1c5f 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -3929,3 +3929,5 @@ QDebug operator<<(QDebug dbg, const QTextFormat &f) #endif QT_END_NAMESPACE + +#include "moc_qtextformat.cpp" diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 8b58f69770..5458e646c5 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -784,8 +784,8 @@ void QTextHtmlParser::parseCloseTag() void QTextHtmlParser::parseExclamationTag() { ++pos; - if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) { - pos += 3; + if (hasPrefix(QLatin1Char('-')) && hasPrefix(QLatin1Char('-'), 1)) { + pos += 2; // eat comments int end = txt.indexOf(QLatin1String("-->"), pos); pos = (end >= 0 ? end + 3 : len); @@ -880,7 +880,7 @@ QString QTextHtmlParser::parseWord() while (pos < len) { QChar c = txt.at(pos++); if (c == QLatin1Char('>') - || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1)) + || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'))) || c == QLatin1Char('<') || c == QLatin1Char('=') || c.isSpace()) { @@ -1677,7 +1677,7 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) node->tableCellRowSpan = qMax(1, node->tableCellRowSpan); } else if (key == QLatin1String("colspan")) { if (setIntAttribute(&node->tableCellColSpan, value)) - node->tableCellColSpan = qMax(1, node->tableCellColSpan); + node->tableCellColSpan = qBound(1, node->tableCellColSpan, 20480); } break; case Html_table: diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 31f558709f..e5622afe9d 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -323,7 +323,9 @@ protected: void applyAttributes(const QStringList &attributes); void eatSpace(); inline bool hasPrefix(QChar c, int lookahead = 0) const - {return pos + lookahead < len && txt.at(pos) == c; } + { + return pos + lookahead < len && txt.at(pos + lookahead) == c; + } int margin(int i, int mar) const; bool nodeIsChildOf(int i, QTextHTMLElements id) const; diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp index 0d87a2135d..e650984c63 100644 --- a/src/gui/text/qtextimagehandler.cpp +++ b/src/gui/text/qtextimagehandler.cpp @@ -53,40 +53,21 @@ QT_BEGIN_NAMESPACE extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio, qreal *sourceDevicePixelRatio); -static QString resolveFileName(QString fileName, QUrl *url, qreal targetDevicePixelRatio, - qreal *sourceDevicePixelRatio) -{ - // We might use the fileName for loading if url loading fails - // try to make sure it is a valid file path. - // Also, QFile{Info}::exists works only on filepaths (not urls) - - if (url->isValid()) { - if (url->scheme() == QLatin1String("qrc")) { - fileName = fileName.right(fileName.length() - 3); - } - else if (url->scheme() == QLatin1String("file")) { - fileName = url->toLocalFile(); - } - } - if (targetDevicePixelRatio <= 1.0) - return fileName; - - // try to find a Nx version - return qt_findAtNxFile(fileName, targetDevicePixelRatio, sourceDevicePixelRatio); +static inline QUrl fromLocalfileOrResources(QString path) +{ + if (path.startsWith(QLatin1String(":/"))) // auto-detect resources and convert them to url + path.prepend(QLatin1String("qrc")); + return QUrl(path); } - static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0) { - QPixmap pm; - - QString name = format.name(); - if (name.startsWith(QLatin1String(":/"))) // auto-detect resources and convert them to url - name.prepend(QLatin1String("qrc")); - QUrl url = QUrl(name); qreal sourcePixelRatio = 1.0; - name = resolveFileName(name, &url, devicePixelRatio, &sourcePixelRatio); + const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio); + const QUrl url = fromLocalfileOrResources(name); + + QPixmap pm; const QVariant data = doc->resource(QTextDocument::ImageResource, url); if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) { pm = qvariant_cast<QPixmap>(data); @@ -161,14 +142,11 @@ static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format) static QImage getImage(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0) { - QImage image; - - QString name = format.name(); - if (name.startsWith(QLatin1String(":/"))) // auto-detect resources - name.prepend(QLatin1String("qrc")); - QUrl url = QUrl(name); qreal sourcePixelRatio = 1.0; - name = resolveFileName(name, &url, devicePixelRatio, &sourcePixelRatio); + const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio); + const QUrl url = fromLocalfileOrResources(name); + + QImage image; const QVariant data = doc->resource(QTextDocument::ImageResource, url); if (data.userType() == QMetaType::QImage) { image = qvariant_cast<QImage>(data); @@ -266,3 +244,5 @@ void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocumen } QT_END_NAMESPACE + +#include "moc_qtextimagehandler_p.cpp" diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index b2f12ef1ce..7e1fb822fc 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -850,6 +850,10 @@ QTextLine QTextLayout::createLine() int l = d->lines.size(); if (l && d->lines.at(l-1).length < 0) { QTextLine(l-1, d).setNumColumns(INT_MAX); + if (d->maxWidth > QFIXED_MAX / 2) { + qWarning("QTextLayout: text too long, truncated."); + return QTextLine(); + } } int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0; int strlen = d->layoutData->string.length(); @@ -1169,10 +1173,17 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang QRectF fullLineRect(tl.rect()); fullLineRect.translate(position); fullLineRect.setRight(QFIXED_MAX); - if (!selectionEndInLine) - region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip)); - if (!selectionStartInLine) - region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip)); + + const bool rightToLeft = d->isRightToLeft(); + + if (!selectionEndInLine) { + region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()) + : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip)); + } + if (!selectionStartInLine) { + region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight()) + : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip)); + } } else if (!selectionEndInLine && isLastLineInBlock &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) { @@ -1325,13 +1336,13 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition bool rightToLeft = d->isRightToLeft(); if (itm >= 0) { const QScriptItem &si = d->layoutData->items.at(itm); - if (si.ascent > 0) + if (si.ascent >= 0) base = si.ascent; - if (si.descent > 0) + if (si.descent >= 0) descent = si.descent; rightToLeft = si.analysis.bidiLevel % 2; } - qreal y = position.y() + (sl.y + sl.base() - base).toReal(); + qreal y = position.y() + (sl.y + sl.base() + sl.descent - base - descent).toReal(); bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) && (p->transform().type() > QTransform::TxTranslate); if (toggleAntialiasing) @@ -1339,7 +1350,20 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition QPainter::CompositionMode origCompositionMode = p->compositionMode(); if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes)) p->setCompositionMode(QPainter::RasterOp_NotDestination); - p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush()); + const QTransform &deviceTransform = p->deviceTransform(); + const qreal xScale = deviceTransform.m11(); + if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) { + p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush()); + } else { + // Ensure consistently rendered cursor width under fractional scaling + const QPen origPen = p->pen(); + QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap); + pen.setCosmetic(true); + const qreal center = x + qreal(width) / 2; + p->setPen(pen); + p->drawLine(QPointF(center, y), QPointF(center, y + (base + descent).toReal())); + p->setPen(origPen); + } p->setCompositionMode(origCompositionMode); if (toggleAntialiasing) p->setRenderHint(QPainter::Antialiasing, false); @@ -1976,7 +2000,8 @@ void QTextLine::layout_helper(int maxGlyphs) if (lbh.currentPosition >= eng->layoutData->string.length() || isBreakableSpace - || attributes[lbh.currentPosition].lineBreak) { + || attributes[lbh.currentPosition].lineBreak + || lbh.tmpData.textWidth >= QFIXED_MAX) { sb_or_ws = true; break; } else if (attributes[lbh.currentPosition].graphemeBoundary) { @@ -2393,14 +2418,18 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const // when we're breaking a RTL script item, since the expected position passed into // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run. if (relativeFrom != (iterator.itemStart - si.position) && !rtl) { - for (int i=itemGlyphsStart; i<glyphsStart; ++i) { - QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); - pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + for (int i = itemGlyphsStart; i < glyphsStart; ++i) { + if (!glyphLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); + pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + } } } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) { - for (int i=itemGlyphsEnd; i>glyphsEnd; --i) { - QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); - pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + for (int i = itemGlyphsEnd; i > glyphsEnd; --i) { + if (!glyphLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); + pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + } } } @@ -2449,8 +2478,10 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const relativeFrom + si.position, relativeTo - relativeFrom + 1)); for (int i = 0; i < subLayout.numGlyphs; ++i) { - QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6); - pos.rx() += (subLayout.advances[i] + justification).toReal(); + if (!subLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6); + pos.rx() += (subLayout.advances[i] + justification).toReal(); + } } if (rtl) @@ -2535,6 +2566,9 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR return; } + static QRectF maxFixedRect(QPointF(-QFIXED_MAX, -QFIXED_MAX), QPointF(QFIXED_MAX, QFIXED_MAX)); + if (!maxFixedRect.contains(pos)) + return; QTextLineItemIterator iterator(eng, index, pos, selection); QFixed lineBase = line.base(); diff --git a/src/gui/text/qtextlist.cpp b/src/gui/text/qtextlist.cpp index 5857afa048..523c22ff87 100644 --- a/src/gui/text/qtextlist.cpp +++ b/src/gui/text/qtextlist.cpp @@ -320,3 +320,5 @@ void QTextList::add(const QTextBlock &block) } QT_END_NAMESPACE + +#include "moc_qtextlist.cpp" diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp index e0d16d2d2a..7296a6fe41 100644 --- a/src/gui/text/qtextmarkdownimporter.cpp +++ b/src/gui/text/qtextmarkdownimporter.cpp @@ -418,6 +418,7 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det) } case MD_SPAN_CODE: charFmt.setFont(m_monoFont); + charFmt.setFontFixedPitch(true); break; case MD_SPAN_DEL: charFmt.setFontStrikeOut(true); @@ -607,6 +608,9 @@ void QTextMarkdownImporter::insertBlock() if (m_doc->isEmpty()) { m_cursor->setBlockFormat(blockFormat); m_cursor->setCharFormat(charFormat); + } else if (m_listItem) { + m_cursor->insertBlock(blockFormat, QTextCharFormat()); + m_cursor->setCharFormat(charFormat); } else { m_cursor->insertBlock(blockFormat, charFormat); } diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp index ae63fcb4dd..3b4d794c1a 100644 --- a/src/gui/text/qtextmarkdownwriter.cpp +++ b/src/gui/text/qtextmarkdownwriter.cpp @@ -531,7 +531,7 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign col += s.length(); } else { QFontInfo fontInfo(fmt.font()); - bool monoFrag = fontInfo.fixedPitch(); + bool monoFrag = fontInfo.fixedPitch() || fmt.fontFixedPitch(); QString markers; if (!ignoreFormat) { if (monoFrag != mono && !m_indentedCodeBlock && !m_fencedCodeBlock) { diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp index 77dcae0dc8..2d307ab30e 100644 --- a/src/gui/text/qtextobject.cpp +++ b/src/gui/text/qtextobject.cpp @@ -1863,3 +1863,5 @@ QString QTextFragment::text() const } QT_END_NAMESPACE + +#include "moc_qtextobject.cpp" diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp index 155ec43c50..b3ec43702c 100644 --- a/src/gui/text/qtextodfwriter.cpp +++ b/src/gui/text/qtextodfwriter.cpp @@ -455,7 +455,7 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF name.prepend(QLatin1String("qrc")); QUrl url = QUrl(name); const QVariant variant = m_document->resource(QTextDocument::ImageResource, url); - if (variant.userType() == QMetaType::QImage) { + if (variant.userType() == QMetaType::QPixmap || variant.userType() == QMetaType::QImage) { image = qvariant_cast<QImage>(variant); } else if (variant.userType() == QMetaType::QByteArray) { data = variant.toByteArray(); @@ -476,7 +476,7 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF QBuffer imageBytes; int imgQuality = imageFormat.quality(); - if (imgQuality >= 100 || imgQuality < 0 || image.hasAlphaChannel()) { + if (imgQuality >= 100 || imgQuality <= 0 || image.hasAlphaChannel()) { QImageWriter imageWriter(&imageBytes, "png"); imageWriter.write(image); diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp index 39f26d5d42..0b656ce8a1 100644 --- a/src/gui/text/qtexttable.cpp +++ b/src/gui/text/qtexttable.cpp @@ -318,13 +318,6 @@ QTextFrame::iterator QTextTableCell::end() const Destroys the table cell. */ -QTextTablePrivate::~QTextTablePrivate() -{ - if (grid) - free(grid); -} - - QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat) { QTextTableFormat fmt = tableFormat; @@ -446,8 +439,7 @@ void QTextTablePrivate::update() const nRows = (cells.size() + nCols-1)/nCols; // qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols); - grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int))); - memset(grid, 0, nRows*nCols*sizeof(int)); + grid.assign(nRows * nCols, 0); QTextDocumentPrivate *p = pieceTable; QTextFormatCollection *c = p->formatCollection(); @@ -470,8 +462,7 @@ void QTextTablePrivate::update() const cellIndices[i] = cell; if (r + rowspan > nRows) { - grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols)); - memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols); + grid.resize((r + rowspan) * nCols, 0); nRows = r + rowspan; } @@ -1344,3 +1335,5 @@ void QTextTable::setFormat(const QTextTableFormat &format) */ QT_END_NAMESPACE + +#include "moc_qtexttable.cpp" diff --git a/src/gui/text/qtexttable_p.h b/src/gui/text/qtexttable_p.h index 5c05611009..784c8824ba 100644 --- a/src/gui/text/qtexttable_p.h +++ b/src/gui/text/qtexttable_p.h @@ -55,14 +55,15 @@ #include "private/qtextobject_p.h" #include "private/qtextdocument_p.h" +#include <vector> + QT_BEGIN_NAMESPACE class QTextTablePrivate : public QTextFramePrivate { Q_DECLARE_PUBLIC(QTextTable) public: - QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), grid(nullptr), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {} - ~QTextTablePrivate(); + QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {} static QTextTable *createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat); void fragmentAdded(QChar type, uint fragment) override; @@ -76,7 +77,7 @@ public: // symmetric to cells array and maps to indecs in grid, // used for fast-lookup for row/column by fragment mutable QVector<int> cellIndices; - mutable int *grid; + mutable std::vector<int> grid; mutable int nRows; mutable int nCols; mutable bool dirty; diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp index 80c0f122e8..feab5b85b9 100644 --- a/src/gui/text/qzip.cpp +++ b/src/gui/text/qzip.cpp @@ -1024,13 +1024,33 @@ bool QZipReader::extractAll(const QString &destinationDir) const // create directories first const QVector<FileInfo> allFiles = fileInfoList(); + bool foundDirs = false; + bool hasDirs = false; for (const FileInfo &fi : allFiles) { const QString absPath = destinationDir + QDir::separator() + fi.filePath; if (fi.isDir) { + foundDirs = true; if (!baseDir.mkpath(fi.filePath)) return false; if (!QFile::setPermissions(absPath, fi.permissions)) return false; + } else if (!hasDirs && fi.filePath.contains(u"/")) { + // filePath does not have leading or trailing '/', so if we find + // one, than the file path contains directories. + hasDirs = true; + } + } + + // Some zip archives can be broken in the sense that they do not report + // separate entries for directories, only for files. In this case we + // need to recreate directory structure based on the file paths. + if (hasDirs && !foundDirs) { + for (const FileInfo &fi : allFiles) { + const auto dirPath = fi.filePath.left(fi.filePath.lastIndexOf(u"/")); + if (!baseDir.mkpath(dirPath)) + return false; + // We will leave the directory permissions default in this case, + // because setting dir permissions based on file is incorrect } } |