1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #ifndef QTEXTENGINE_P_H
43 #define QTEXTENGINE_P_H
49 // This file is not part of the Qt API. It exists for the convenience
50 // of other Qt classes. This header file may change from version to
51 // version without notice, or even be removed.
56 #include "QtCore/qglobal.h"
57 #include "QtCore/qstring.h"
58 #include "QtCore/qvarlengtharray.h"
59 #include "QtCore/qnamespace.h"
60 #include "QtGui/qtextlayout.h"
61 #include "private/qtextformat_p.h"
62 #include "private/qfont_p.h"
63 #include "QtCore/qvector.h"
64 #include "QtGui/qpaintengine.h"
65 #include "QtGui/qtextobject.h"
66 #include "QtGui/qtextoption.h"
67 #include "QtGui/qtextcursor.h"
68 #include "QtCore/qset.h"
69 #include "QtCore/qdebug.h"
70 #ifndef QT_BUILD_COMPAT_LIB
71 #include "private/qtextdocument_p.h"
74 #include "private/qharfbuzz_copy_p.h"
76 #include "private/qfixed_p.h"
88 class QAbstractTextDocumentLayout;
91 // this uses the same coordinate system as Qt, but a different one to freetype.
92 // * y is usually negative, and is equal to the ascent.
93 // * negative yoff means the following stuff is drawn higher up.
94 // the characters bounding rect is given by QRect(x,y,width,height), its advance by
96 struct Q_GUI_EXPORT glyph_metrics_t
98 inline glyph_metrics_t()
99 : x(100000), y(100000) {}
100 inline glyph_metrics_t(QFixed _x, QFixed _y, QFixed _width, QFixed _height, QFixed _xoff, QFixed _yoff)
115 glyph_metrics_t transformed(const QTransform &xform) const;
116 inline bool isValid() const {return x != 100000 && y != 100000;}
118 Q_DECLARE_TYPEINFO(glyph_metrics_t, Q_PRIMITIVE_TYPE);
120 struct Q_AUTOTEST_EXPORT QScriptAnalysis
127 LineOrParagraphSeparator = 4,
129 SpaceTabOrObject = Space,
134 unsigned short script : 7;
135 unsigned short bidiLevel : 6; // Unicode Bidi algorithm embedding level (0-61)
136 unsigned short flags : 3;
137 inline bool operator == (const QScriptAnalysis &other) const {
138 return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags;
141 Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE);
143 struct QGlyphJustification
145 inline QGlyphJustification()
146 : type(0), nKashidas(0), space_18d6(0)
149 enum JustificationType {
156 uint nKashidas : 6; // more do not make sense...
157 uint space_18d6 : 24;
159 Q_DECLARE_TYPEINFO(QGlyphJustification, Q_PRIMITIVE_TYPE);
161 struct QGlyphLayoutInstance
166 QGlyphJustification justification;
167 HB_GlyphAttributes attributes;
172 // init to 0 not needed, done when shaping
173 QFixedPoint *offsets; // 8 bytes per element
174 HB_Glyph *glyphs; // 4 bytes per element
175 QFixed *advances_x; // 4 bytes per element
176 QFixed *advances_y; // 4 bytes per element
177 QGlyphJustification *justifications; // 4 bytes per element
178 HB_GlyphAttributes *attributes; // 2 bytes per element
182 inline QGlyphLayout() : numGlyphs(0) {}
184 inline explicit QGlyphLayout(char *address, int totalGlyphs)
186 offsets = reinterpret_cast<QFixedPoint *>(address);
187 int offset = totalGlyphs * sizeof(HB_FixedPoint);
188 glyphs = reinterpret_cast<HB_Glyph *>(address + offset);
189 offset += totalGlyphs * sizeof(HB_Glyph);
190 advances_x = reinterpret_cast<QFixed *>(address + offset);
191 offset += totalGlyphs * sizeof(QFixed);
192 advances_y = reinterpret_cast<QFixed *>(address + offset);
193 offset += totalGlyphs * sizeof(QFixed);
194 justifications = reinterpret_cast<QGlyphJustification *>(address + offset);
195 offset += totalGlyphs * sizeof(QGlyphJustification);
196 attributes = reinterpret_cast<HB_GlyphAttributes *>(address + offset);
197 numGlyphs = totalGlyphs;
200 inline QGlyphLayout mid(int position, int n = -1) const {
201 QGlyphLayout copy = *this;
202 copy.glyphs += position;
203 copy.advances_x += position;
204 copy.advances_y += position;
205 copy.offsets += position;
206 copy.justifications += position;
207 copy.attributes += position;
209 copy.numGlyphs -= position;
215 static inline int spaceNeededForGlyphLayout(int totalGlyphs) {
216 return totalGlyphs * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes)
217 + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint)
218 + sizeof(QGlyphJustification));
221 inline QFixed effectiveAdvance(int item) const
222 { return (advances_x[item] + QFixed::fromFixed(justifications[item].space_18d6)) * !attributes[item].dontPrint; }
224 inline QGlyphLayoutInstance instance(int position) const {
225 QGlyphLayoutInstance g;
226 g.offset.x = offsets[position].x;
227 g.offset.y = offsets[position].y;
228 g.glyph = glyphs[position];
229 g.advance.x = advances_x[position];
230 g.advance.y = advances_y[position];
231 g.justification = justifications[position];
232 g.attributes = attributes[position];
236 inline void setInstance(int position, const QGlyphLayoutInstance &g) {
237 offsets[position].x = g.offset.x;
238 offsets[position].y = g.offset.y;
239 glyphs[position] = g.glyph;
240 advances_x[position] = g.advance.x;
241 advances_y[position] = g.advance.y;
242 justifications[position] = g.justification;
243 attributes[position] = g.attributes;
246 inline void clear(int first = 0, int last = -1) {
249 if (first == 0 && last == numGlyphs
250 && reinterpret_cast<char *>(offsets + numGlyphs) == reinterpret_cast<char *>(glyphs)) {
251 memset(offsets, 0, spaceNeededForGlyphLayout(numGlyphs));
253 const int num = last - first;
254 memset(offsets + first, 0, num * sizeof(QFixedPoint));
255 memset(glyphs + first, 0, num * sizeof(HB_Glyph));
256 memset(advances_x + first, 0, num * sizeof(QFixed));
257 memset(advances_y + first, 0, num * sizeof(QFixed));
258 memset(justifications + first, 0, num * sizeof(QGlyphJustification));
259 memset(attributes + first, 0, num * sizeof(HB_GlyphAttributes));
263 inline char *data() {
264 return reinterpret_cast<char *>(offsets);
267 void grow(char *address, int totalGlyphs);
270 class QVarLengthGlyphLayoutArray : private QVarLengthArray<void *>, public QGlyphLayout
273 typedef QVarLengthArray<void *> Array;
275 QVarLengthGlyphLayoutArray(int totalGlyphs)
276 : Array(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1)
277 , QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs)
279 memset(Array::data(), 0, Array::size() * sizeof(void *));
282 void resize(int totalGlyphs)
284 Array::resize(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1);
286 *((QGlyphLayout *)this) = QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs);
287 memset(Array::data(), 0, Array::size() * sizeof(void *));
291 template <int N> struct QGlyphLayoutArray : public QGlyphLayout
295 : QGlyphLayout(reinterpret_cast<char *>(buffer), N)
297 memset(buffer, 0, sizeof(buffer));
301 void *buffer[(N * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes)
302 + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint)
303 + sizeof(QGlyphJustification)))
304 / sizeof(void *) + 1];
308 /// Internal QTextItem
309 class QTextItemInt : public QTextItem
312 inline QTextItemInt()
313 : justified(false), underlineStyle(QTextCharFormat::NoUnderline), num_chars(0), chars(0),
314 logClusters(0), f(0), fontEngine(0)
316 QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format = QTextCharFormat());
317 QTextItemInt(const QGlyphLayout &g, QFont *font, const QChar *chars, int numChars, QFontEngine *fe,
318 const QTextCharFormat &format = QTextCharFormat());
320 /// copy the structure items, adjusting the glyphs arrays to the right subarrays.
321 /// the width of the returned QTextItemInt is not adjusted, for speed reasons
322 QTextItemInt midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const;
323 void initWithScriptItem(const QScriptItem &si);
331 QTextCharFormat::UnderlineStyle underlineStyle;
332 const QTextCharFormat charFormat;
335 const unsigned short *logClusters;
339 QFontEngine *fontEngine;
342 struct Q_AUTOTEST_EXPORT QScriptItem
346 num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1),
347 glyph_data_offset(0) {}
348 inline QScriptItem(int p, const QScriptAnalysis &a)
349 : position(p), analysis(a),
350 num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1),
351 glyph_data_offset(0) {}
354 QScriptAnalysis analysis;
355 unsigned short num_glyphs;
360 int glyph_data_offset;
361 QFixed height() const { return ascent + descent; }
365 Q_DECLARE_TYPEINFO(QScriptItem, Q_MOVABLE_TYPE);
367 typedef QVector<QScriptItem> QScriptItemArray;
369 struct Q_AUTOTEST_EXPORT QScriptLine
371 // created and filled in QTextLine::layout_helper
373 : from(0), trailingSpaces(0), length(0),
374 justified(0), gridfitted(0),
375 hasTrailingSpaces(0), leadingIncluded(0) {}
385 unsigned short trailingSpaces;
386 signed int length : 28;
387 mutable uint justified : 1;
388 mutable uint gridfitted : 1;
389 uint hasTrailingSpaces : 1;
390 uint leadingIncluded : 1;
391 QFixed height() const { return (ascent + descent).ceil()
392 + (leadingIncluded? qMax(QFixed(),leading) : QFixed()); }
393 QFixed base() const { return ascent
394 + (leadingIncluded ? qMax(QFixed(),leading) : QFixed()); }
395 void setDefaultHeight(QTextEngine *eng);
396 void operator+=(const QScriptLine &other);
398 Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE);
401 inline void QScriptLine::operator+=(const QScriptLine &other)
403 leading= qMax(leading + ascent, other.leading + other.ascent) - qMax(ascent, other.ascent);
404 descent = qMax(descent, other.descent);
405 ascent = qMax(ascent, other.ascent);
406 textWidth += other.textWidth;
407 length += other.length;
410 typedef QVector<QScriptLine> QScriptLineArray;
413 class QTextFormatCollection;
415 class Q_GUI_EXPORT QTextEngine {
422 struct Q_GUI_EXPORT LayoutData {
423 LayoutData(const QString &str, void **stack_memory, int mem_size);
426 mutable QScriptItemArray items;
428 int available_glyphs;
430 unsigned short *logClustersPtr;
431 QGlyphLayout glyphLayout;
434 uint layoutState : 2;
435 uint memory_on_stack : 1;
436 uint haveCharAttributes : 1;
438 bool reallocate(int totalGlyphs);
441 struct ItemDecoration {
442 ItemDecoration(qreal x1, qreal x2, qreal y, const QPen &pen):
443 x1(x1), x2(x2), y(y), pen(pen) {}
451 typedef QList<ItemDecoration> ItemDecorationList;
453 QTextEngine(LayoutData *data);
455 QTextEngine(const QString &str, const QFont &f);
462 // keep in sync with QAbstractFontEngine::TextShapingFlag!!
464 RightToLeft = 0x0001,
465 DesignMetrics = 0x0002,
466 GlyphIndicesOnly = 0x0004
468 Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag)
471 void clearLineData();
473 void validate() const;
474 void itemize() const;
476 bool isRightToLeft() const;
477 static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
479 const HB_CharAttributes *attributes() const;
481 void shape(int item) const;
483 void justify(const QScriptLine &si);
484 QFixed alignLine(const QScriptLine &line);
486 QFixed width(int charFrom, int numChars) const;
487 glyph_metrics_t boundingBox(int from, int len) const;
488 glyph_metrics_t tightBoundingBox(int from, int len) const;
490 int length(int item) const {
491 const QScriptItem &si = layoutData->items[item];
492 int from = si.position;
494 return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from;
496 int length(const QScriptItem *si) const {
498 if (si + 1 < layoutData->items.constData()+ layoutData->items.size())
499 end = (si+1)->position;
501 end = layoutData->string.length();
502 return end - si->position;
505 QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0, QFixed *leading = 0) const;
506 QFont font(const QScriptItem &si) const;
507 inline QFont font() const { return fnt; }
510 * Returns a pointer to an array of log clusters, offset at the script item.
511 * Each item in the array is a unsigned short. For each character in the original string there is an entry in the table
512 * so there is a one to one correlation in indexes between the original text and the index in the logcluster.
513 * The value of each item is the position in the glyphs array. Multiple similar pointers in the logclusters array imply
514 * that one glyph is used for more than one character.
517 inline unsigned short *logClusters(const QScriptItem *si) const
518 { return layoutData->logClustersPtr+si->position; }
520 * Returns an array of QGlyphLayout items, offset at the script item.
521 * Each item in the array matches one glyph in the text, storing the advance, position etc.
522 * The returned item's length equals to the number of available glyphs. This may be more
523 * than what was actually shaped.
526 inline QGlyphLayout availableGlyphs(const QScriptItem *si) const {
527 return layoutData->glyphLayout.mid(si->glyph_data_offset);
530 * Returns an array of QGlyphLayout items, offset at the script item.
531 * Each item in the array matches one glyph in the text, storing the advance, position etc.
532 * The returned item's length equals to the number of shaped glyphs.
535 inline QGlyphLayout shapedGlyphs(const QScriptItem *si) const {
536 return layoutData->glyphLayout.mid(si->glyph_data_offset, si->num_glyphs);
539 inline bool ensureSpace(int nGlyphs) const {
540 if (layoutData->glyphLayout.numGlyphs - layoutData->used < nGlyphs)
541 return layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4);
547 int findItem(int strPos) const;
548 inline QTextFormatCollection *formats() const {
549 #ifdef QT_BUILD_COMPAT_LIB
550 return 0; // Compat should never reference this symbol
552 return block.docHandle()->formatCollection();
555 QTextCharFormat format(const QScriptItem *si) const;
556 inline QAbstractTextDocumentLayout *docLayout() const {
557 #ifdef QT_BUILD_COMPAT_LIB
558 return 0; // Compat should never reference this symbol
560 return block.docHandle()->document()->documentLayout();
563 int formatIndex(const QScriptItem *si) const;
565 /// returns the width of tab at index (in the tabs array) with the tab-start at position x
566 QFixed calculateTabWidth(int index, QFixed x) const;
568 mutable QScriptLineArray lines;
570 struct FontEngineCache {
572 mutable QFontEngine *prevFontEngine;
573 mutable QFontEngine *prevScaledFontEngine;
574 mutable int prevScript;
575 mutable int prevPosition;
576 mutable int prevLength;
577 inline void reset() {
579 prevScaledFontEngine = 0;
585 mutable FontEngineCache feCache;
589 #ifndef QT_NO_RAWFONT
600 uint cacheGlyphs : 1;
601 uint stackEngine : 1;
602 uint forceJustification : 1;
603 uint visualMovement : 1;
604 uint delayDecorations: 1;
605 #ifndef QT_NO_RAWFONT
609 int *underlinePositions;
611 mutable LayoutData *layoutData;
613 ItemDecorationList underlineList;
614 ItemDecorationList strikeOutList;
615 ItemDecorationList overlineList;
617 inline bool hasFormats() const { return (block.docHandle() || specialData); }
618 inline bool visualCursorMovement() const
620 return (visualMovement ||
621 (block.docHandle() ? block.docHandle()->defaultCursorMoveStyle == Qt::VisualMoveStyle : false));
627 QList<QTextLayout::FormatRange> addFormats;
628 QVector<int> addFormatIndices;
629 QVector<int> resolvedFormatIndices;
631 SpecialData *specialData;
633 bool atWordSeparator(int position) const;
634 bool atSpace(int position) const;
635 void indexAdditionalFormats();
637 QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0, int from = 0, int count = -1) const;
639 void shapeLine(const QScriptLine &line);
640 QFixed leadingSpaceWidth(const QScriptLine &line);
642 QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
643 int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter);
644 int previousLogicalPosition(int oldPos) const;
645 int nextLogicalPosition(int oldPos) const;
646 int lineNumberForTextPosition(int pos);
647 int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
648 void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints);
649 void resetFontEngineCache();
651 void enableDelayDecorations(bool enable = true) { delayDecorations = enable; }
653 void addUnderline(QPainter *painter, const QLineF &line);
654 void addStrikeOut(QPainter *painter, const QLineF &line);
655 void addOverline(QPainter *painter, const QLineF &line);
657 void drawDecorations(QPainter *painter);
658 void clearDecorations();
659 void adjustUnderlines();
662 void addItemDecoration(QPainter *painter, const QLineF &line, ItemDecorationList *decorationList);
663 void adjustUnderlines(ItemDecorationList::iterator start,
664 ItemDecorationList::iterator end,
665 qreal underlinePos, qreal penWidth);
666 void drawItemDecorationList(QPainter *painter, const ItemDecorationList &decorationList);
667 void setBoundary(int strPos) const;
668 void addRequiredBoundaries() const;
669 void shapeText(int item) const;
670 void shapeTextWithHarfbuzz(int item) const;
671 void splitItem(int item, int pos) const;
673 void resolveAdditionalFormats() const;
674 int endOfLine(int lineNum);
675 int beginningOfLine(int lineNum);
676 int getClusterLength(unsigned short *logClusters, const HB_CharAttributes *attributes, int from, int to, int glyph_pos, int *start);
679 class Q_GUI_EXPORT QStackTextEngine : public QTextEngine {
681 enum { MemSize = 256*40/sizeof(void *) };
682 QStackTextEngine(const QString &string, const QFont &f);
683 LayoutData _layoutData;
684 void *_memory[MemSize];
687 struct QTextLineItemIterator
689 QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
690 const QTextLayout::FormatRange *_selection = 0);
692 inline bool atEnd() const { return logicalItem >= nItems - 1; }
693 inline bool atBeginning() const { return logicalItem <= 0; }
696 bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
697 inline bool isOutsideSelection() const {
699 return !getSelectionBounds(&tmp1, &tmp2);
706 const QScriptLine &line;
725 QVarLengthArray<int> visualOrder;
726 QVarLengthArray<uchar> levels;
728 const QTextLayout::FormatRange *selection;
731 Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
735 #endif // QTEXTENGINE_P_H