diff -r 000000000000 -r 1918ee327afb src/gui/text/qtextengine_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui/text/qtextengine_p.h Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,609 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEXTENGINE_P_H +#define QTEXTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qglobal.h" +#include "QtCore/qstring.h" +#include "QtCore/qvarlengtharray.h" +#include "QtCore/qnamespace.h" +#include "QtGui/qtextlayout.h" +#include "private/qtextformat_p.h" +#include "private/qfont_p.h" +#include "QtCore/qvector.h" +#include "QtGui/qpaintengine.h" +#include "QtGui/qtextobject.h" +#include "QtGui/qtextoption.h" +#include "QtCore/qset.h" +#include "QtCore/qdebug.h" +#ifndef QT_BUILD_COMPAT_LIB +#include "private/qtextdocument_p.h" +#endif +#include "private/qharfbuzz_p.h" +#include "private/qfixed_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QFontPrivate; +class QFontEngine; + +class QString; +class QPainter; + +class QAbstractTextDocumentLayout; + + +// this uses the same coordinate system as Qt, but a different one to freetype. +// * y is usually negative, and is equal to the ascent. +// * negative yoff means the following stuff is drawn higher up. +// the characters bounding rect is given by QRect(x,y,width,height), its advance by +// xoo and yoff +struct glyph_metrics_t +{ + inline glyph_metrics_t() + : x(100000), y(100000) {} + inline glyph_metrics_t(QFixed _x, QFixed _y, QFixed _width, QFixed _height, QFixed _xoff, QFixed _yoff) + : x(_x), + y(_y), + width(_width), + height(_height), + xoff(_xoff), + yoff(_yoff) + {} + QFixed x; + QFixed y; + QFixed width; + QFixed height; + QFixed xoff; + QFixed yoff; + + glyph_metrics_t transformed(const QTransform &xform) const; + inline bool isValid() const {return x != 100000 && y != 100000;} +}; +Q_DECLARE_TYPEINFO(glyph_metrics_t, Q_PRIMITIVE_TYPE); + +struct Q_AUTOTEST_EXPORT QScriptAnalysis +{ + enum Flags { + None = 0, + Lowercase = 1, + Uppercase = 2, + SmallCaps = 3, + LineOrParagraphSeparator = 4, + Space = 5, + SpaceTabOrObject = Space, + Tab = 6, + TabOrObject = Tab, + Object = 7 + }; + unsigned short script : 8; + unsigned short bidiLevel : 6; // Unicode Bidi algorithm embedding level (0-61) + unsigned short flags : 3; + inline bool operator == (const QScriptAnalysis &other) const { + return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags; + } +}; +Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE); + +struct QGlyphJustification +{ + inline QGlyphJustification() + : type(0), nKashidas(0), space_18d6(0) + {} + + enum JustificationType { + JustifyNone, + JustifySpace, + JustifyKashida + }; + + uint type :2; + uint nKashidas : 6; // more do not make sense... + uint space_18d6 : 24; +}; +Q_DECLARE_TYPEINFO(QGlyphJustification, Q_PRIMITIVE_TYPE); + +struct QGlyphLayoutInstance +{ + QFixedPoint offset; + QFixedPoint advance; + HB_Glyph glyph; + QGlyphJustification justification; + HB_GlyphAttributes attributes; +}; + +struct QGlyphLayout +{ + // init to 0 not needed, done when shaping + QFixedPoint *offsets; // 8 bytes per element + HB_Glyph *glyphs; // 4 bytes per element + QFixed *advances_x; // 4 bytes per element + QFixed *advances_y; // 4 bytes per element + QGlyphJustification *justifications; // 4 bytes per element + HB_GlyphAttributes *attributes; // 2 bytes per element + + int numGlyphs; + + inline QGlyphLayout() : numGlyphs(0) {} + + inline explicit QGlyphLayout(char *address, int totalGlyphs) + { + offsets = reinterpret_cast(address); + int offset = totalGlyphs * sizeof(HB_FixedPoint); + glyphs = reinterpret_cast(address + offset); + offset += totalGlyphs * sizeof(HB_Glyph); + advances_x = reinterpret_cast(address + offset); + offset += totalGlyphs * sizeof(QFixed); + advances_y = reinterpret_cast(address + offset); + offset += totalGlyphs * sizeof(QFixed); + justifications = reinterpret_cast(address + offset); + offset += totalGlyphs * sizeof(QGlyphJustification); + attributes = reinterpret_cast(address + offset); + numGlyphs = totalGlyphs; + } + + inline QGlyphLayout mid(int position, int n = -1) const { + QGlyphLayout copy = *this; + copy.glyphs += position; + copy.advances_x += position; + copy.advances_y += position; + copy.offsets += position; + copy.justifications += position; + copy.attributes += position; + if (n == -1) + copy.numGlyphs -= position; + else + copy.numGlyphs = n; + return copy; + } + + static inline int spaceNeededForGlyphLayout(int totalGlyphs) { + return totalGlyphs * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes) + + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint) + + sizeof(QGlyphJustification)); + } + + inline QFixed effectiveAdvance(int item) const + { return (advances_x[item] + QFixed::fromFixed(justifications[item].space_18d6)) * !attributes[item].dontPrint; } + + inline QGlyphLayoutInstance instance(int position) const { + QGlyphLayoutInstance g; + g.offset.x = offsets[position].x; + g.offset.y = offsets[position].y; + g.glyph = glyphs[position]; + g.advance.x = advances_x[position]; + g.advance.y = advances_y[position]; + g.justification = justifications[position]; + g.attributes = attributes[position]; + return g; + } + + inline void setInstance(int position, const QGlyphLayoutInstance &g) { + offsets[position].x = g.offset.x; + offsets[position].y = g.offset.y; + glyphs[position] = g.glyph; + advances_x[position] = g.advance.x; + advances_y[position] = g.advance.y; + justifications[position] = g.justification; + attributes[position] = g.attributes; + } + + inline void clear(int first = 0, int last = -1) { + if (last == -1) + last = numGlyphs; + if (first == 0 && last == numGlyphs + && reinterpret_cast(offsets + numGlyphs) == reinterpret_cast(glyphs)) { + memset(offsets, 0, spaceNeededForGlyphLayout(numGlyphs)); + } else { + const int num = last - first; + memset(offsets + first, 0, num * sizeof(QFixedPoint)); + memset(glyphs + first, 0, num * sizeof(HB_Glyph)); + memset(advances_x + first, 0, num * sizeof(QFixed)); + memset(advances_y + first, 0, num * sizeof(QFixed)); + memset(justifications + first, 0, num * sizeof(QGlyphJustification)); + memset(attributes + first, 0, num * sizeof(HB_GlyphAttributes)); + } + } + + inline char *data() { + return reinterpret_cast(offsets); + } + + void grow(char *address, int totalGlyphs); +}; + +class QVarLengthGlyphLayoutArray : private QVarLengthArray, public QGlyphLayout +{ +private: + typedef QVarLengthArray Array; +public: + QVarLengthGlyphLayoutArray(int totalGlyphs) + : Array(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1) + , QGlyphLayout(reinterpret_cast(Array::data()), totalGlyphs) + { + memset(Array::data(), 0, Array::size() * sizeof(void *)); + } + + void resize(int totalGlyphs) + { + Array::resize(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1); + + *((QGlyphLayout *)this) = QGlyphLayout(reinterpret_cast(Array::data()), totalGlyphs); + memset(Array::data(), 0, Array::size() * sizeof(void *)); + } +}; + +template struct QGlyphLayoutArray : public QGlyphLayout +{ +public: + QGlyphLayoutArray() + : QGlyphLayout(reinterpret_cast(buffer), N) + { + memset(buffer, 0, sizeof(buffer)); + } + +private: + void *buffer[(N * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes) + + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint) + + sizeof(QGlyphJustification))) + / sizeof(void *) + 1]; +}; + +struct QScriptItem; +/// Internal QTextItem +class QTextItemInt : public QTextItem +{ +public: + inline QTextItemInt() + : justified(false), underlineStyle(QTextCharFormat::NoUnderline), num_chars(0), chars(0), + logClusters(0), f(0), fontEngine(0) + {} + QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format = QTextCharFormat()); + + /// copy the structure items, adjusting the glyphs arrays to the right subarrays. + /// the width of the returned QTextItemInt is not adjusted, for speed reasons + QTextItemInt midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const; + + QFixed descent; + QFixed ascent; + QFixed width; + + RenderFlags flags; + bool justified; + QTextCharFormat::UnderlineStyle underlineStyle; + const QTextCharFormat charFormat; + int num_chars; + const QChar *chars; + const unsigned short *logClusters; + const QFont *f; + + QGlyphLayout glyphs; + QFontEngine *fontEngine; +}; + +inline bool qIsControlChar(ushort uc) +{ + return uc >= 0x200b && uc <= 0x206f + && (uc <= 0x200f /* ZW Space, ZWNJ, ZWJ, LRM and RLM */ + || (uc >= 0x2028 && uc <= 0x202f /* LS, PS, LRE, RLE, PDF, LRO, RLO, NNBSP */) + || uc >= 0x206a /* ISS, ASS, IAFS, AFS, NADS, NODS */); +} + +struct Q_AUTOTEST_EXPORT QScriptItem +{ + inline QScriptItem() + : position(0), + num_glyphs(0), descent(-1), ascent(-1), width(-1), + glyph_data_offset(0) {} + inline QScriptItem(int p, const QScriptAnalysis &a) + : position(p), analysis(a), + num_glyphs(0), descent(-1), ascent(-1), width(-1), + glyph_data_offset(0) {} + + int position; + QScriptAnalysis analysis; + unsigned short num_glyphs; + QFixed descent; + QFixed ascent; + QFixed width; + int glyph_data_offset; + QFixed height() const { return ascent + descent + 1; } +}; + + +Q_DECLARE_TYPEINFO(QScriptItem, Q_MOVABLE_TYPE); + +typedef QVector QScriptItemArray; + +struct Q_AUTOTEST_EXPORT QScriptLine +{ + // created and filled in QTextLine::layout_helper + QScriptLine() + : from(0), length(0), + justified(0), gridfitted(0), + hasTrailingSpaces(0) {} + QFixed descent; + QFixed ascent; + QFixed x; + QFixed y; + QFixed width; + QFixed textWidth; + int from; + signed int length : 29; + mutable uint justified : 1; + mutable uint gridfitted : 1; + uint hasTrailingSpaces : 1; + QFixed height() const { return ascent + descent + 1; } + void setDefaultHeight(QTextEngine *eng); + void operator+=(const QScriptLine &other); +}; +Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE); + + +inline void QScriptLine::operator+=(const QScriptLine &other) +{ + descent = qMax(descent, other.descent); + ascent = qMax(ascent, other.ascent); + textWidth += other.textWidth; + length += other.length; +} + +typedef QVector QScriptLineArray; + +class QFontPrivate; +class QTextFormatCollection; + +class Q_GUI_EXPORT QTextEngine { +public: + struct LayoutData { + LayoutData(const QString &str, void **stack_memory, int mem_size); + LayoutData(); + ~LayoutData(); + mutable QScriptItemArray items; + int allocated; + int available_glyphs; + void **memory; + unsigned short *logClustersPtr; + QGlyphLayout glyphLayout; + mutable int used; + uint hasBidi : 1; + uint inLayout : 1; + uint memory_on_stack : 1; + bool haveCharAttributes; + QString string; + void reallocate(int totalGlyphs); + }; + + QTextEngine(LayoutData *data); + QTextEngine(); + QTextEngine(const QString &str, const QFont &f); + ~QTextEngine(); + + enum Mode { + WidthOnly = 0x07 + }; + + // keep in sync with QAbstractFontEngine::TextShapingFlag!! + enum ShaperFlag { + RightToLeft = 0x0001, + DesignMetrics = 0x0002, + GlyphIndicesOnly = 0x0004 + }; + Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag) + + void invalidate(); + void clearLineData(); + + void validate() const; + void itemize() const; + + static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder); + + const HB_CharAttributes *attributes() const; + + void shape(int item) const; + + void justify(const QScriptLine &si); + + QFixed width(int charFrom, int numChars) const; + glyph_metrics_t boundingBox(int from, int len) const; + glyph_metrics_t tightBoundingBox(int from, int len) const; + + int length(int item) const { + const QScriptItem &si = layoutData->items[item]; + int from = si.position; + item++; + return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from; + } + int length(const QScriptItem *si) const { + int end; + if (si + 1 < layoutData->items.constData()+ layoutData->items.size()) + end = (si+1)->position; + else + end = layoutData->string.length(); + return end - si->position; + } + + QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0) const; + QFont font(const QScriptItem &si) const; + inline QFont font() const { return fnt; } + + /** + * Returns a pointer to an array of log clusters, offset at the script item. + * Each item in the array is a unsigned short. For each character in the original string there is an entry in the table + * so there is a one to one correlation in indexes between the original text and the index in the logcluster. + * The value of each item is the position in the glyphs array. Multiple similar pointers in the logclusters array imply + * that one glyph is used for more than one character. + * \sa glyphs() + */ + inline unsigned short *logClusters(const QScriptItem *si) const + { return layoutData->logClustersPtr+si->position; } + /** + * Returns an array of QGlyphLayout items, offset at the script item. + * Each item in the array matches one glyph in the text, storing the advance, position etc. + * The returned item's length equals to the number of available glyphs. This may be more + * than what was actually shaped. + * \sa logClusters() + */ + inline QGlyphLayout availableGlyphs(const QScriptItem *si) const { + return layoutData->glyphLayout.mid(si->glyph_data_offset); + } + /** + * Returns an array of QGlyphLayout items, offset at the script item. + * Each item in the array matches one glyph in the text, storing the advance, position etc. + * The returned item's length equals to the number of shaped glyphs. + * \sa logClusters() + */ + inline QGlyphLayout shapedGlyphs(const QScriptItem *si) const { + return layoutData->glyphLayout.mid(si->glyph_data_offset, si->num_glyphs); + } + + inline void ensureSpace(int nGlyphs) const { + if (layoutData->glyphLayout.numGlyphs - layoutData->used < nGlyphs) + layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4); + } + + void freeMemory(); + + int findItem(int strPos) const; + inline QTextFormatCollection *formats() const { +#ifdef QT_BUILD_COMPAT_LIB + return 0; // Compat should never reference this symbol +#else + return block.docHandle()->formatCollection(); +#endif + } + QTextCharFormat format(const QScriptItem *si) const; + inline QAbstractTextDocumentLayout *docLayout() const { +#ifdef QT_BUILD_COMPAT_LIB + return 0; // Compat should never reference this symbol +#else + return block.docHandle()->document()->documentLayout(); +#endif + } + int formatIndex(const QScriptItem *si) const; + + /// returns the width of tab at index (in the tabs array) with the tab-start at position x + QFixed calculateTabWidth(int index, QFixed x) const; + + mutable QScriptLineArray lines; + + QString text; + QFont fnt; + QTextBlock block; + + QTextOption option; + + QFixed minWidth; + QFixed maxWidth; + QPointF position; + uint ignoreBidi : 1; + uint cacheGlyphs : 1; + uint stackEngine : 1; + uint forceJustification : 1; + + int *underlinePositions; + + mutable LayoutData *layoutData; + + inline bool hasFormats() const { return (block.docHandle() || specialData); } + + struct SpecialData { + int preeditPosition; + QString preeditText; + QList addFormats; + QVector addFormatIndices; + QVector resolvedFormatIndices; + }; + SpecialData *specialData; + + bool atWordSeparator(int position) const; + bool atSpace(int position) const; + void indexAdditionalFormats(); + + QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0) const; + + void shapeLine(const QScriptLine &line); + +private: + void setBoundary(int strPos) const; + void addRequiredBoundaries() const; + void shapeText(int item) const; + void shapeTextWithHarfbuzz(int item) const; +#if defined(Q_WS_WINCE) + void shapeTextWithCE(int item) const; +#endif +#if defined(Q_WS_MAC) + void shapeTextMac(int item) const; +#endif + void splitItem(int item, int pos) const; + + void resolveAdditionalFormats() const; +}; + +class QStackTextEngine : public QTextEngine { +public: + enum { MemSize = 256*40/sizeof(void *) }; + QStackTextEngine(const QString &string, const QFont &f); + LayoutData _layoutData; + void *_memory[MemSize]; +}; + + +Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags) + +QT_END_NAMESPACE + +#endif // QTEXTENGINE_P_H