1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QTEXTENGINE_P_H
41 #define QTEXTENGINE_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists for the convenience
48 // of other Qt classes.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtGui/private/qtguiglobal_p.h>
55 #include "QtCore/qstring.h"
56 #include "QtCore/qvarlengtharray.h"
57 #include "QtCore/qnamespace.h"
58 #include "QtGui/qtextlayout.h"
59 #include "private/qtextformat_p.h"
60 #include "private/qfont_p.h"
61 #include "QtCore/qvector.h"
62 #include "QtGui/qpaintengine.h"
63 #include "QtGui/qtextobject.h"
64 #include "QtGui/qtextoption.h"
65 #include "QtGui/qtextcursor.h"
66 #include "QtCore/qset.h"
67 #include "QtCore/qdebug.h"
68 #ifndef QT_BUILD_COMPAT_LIB
69 #include "private/qtextdocument_p.h"
70 #endif
71 
72 #include "private/qfixed_p.h"
73 
74 #include <private/qunicodetools_p.h>
75 
76 #include <stdlib.h>
77 #include <vector>
78 
79 QT_BEGIN_NAMESPACE
80 
81 class QFontPrivate;
82 class QFontEngine;
83 
84 class QString;
85 class QPainter;
86 
87 class QAbstractTextDocumentLayout;
88 
89 typedef quint32 glyph_t;
90 
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
95 // xoo and yoff
96 struct Q_GUI_EXPORT glyph_metrics_t
97 {
glyph_metrics_tglyph_metrics_t98     inline glyph_metrics_t()
99         : x(100000),  y(100000) {}
glyph_metrics_tglyph_metrics_t100     inline glyph_metrics_t(QFixed _x, QFixed _y, QFixed _width, QFixed _height, QFixed _xoff, QFixed _yoff)
101         : x(_x),
102           y(_y),
103           width(_width),
104           height(_height),
105           xoff(_xoff),
106           yoff(_yoff)
107         {}
108     QFixed x;
109     QFixed y;
110     QFixed width;
111     QFixed height;
112     QFixed xoff;
113     QFixed yoff;
114 
115     glyph_metrics_t transformed(const QTransform &xform) const;
isValidglyph_metrics_t116     inline bool isValid() const {return x != 100000 && y != 100000;}
117 
leftBearingglyph_metrics_t118     inline QFixed leftBearing() const
119     {
120         if (!isValid())
121             return QFixed();
122 
123         return x;
124     }
125 
rightBearingglyph_metrics_t126     inline QFixed rightBearing() const
127     {
128         if (!isValid())
129             return QFixed();
130 
131         return xoff - x - width;
132     }
133 };
134 Q_DECLARE_TYPEINFO(glyph_metrics_t, Q_PRIMITIVE_TYPE);
135 
136 struct Q_AUTOTEST_EXPORT QScriptAnalysis
137 {
138     enum Flags {
139         None = 0,
140         Lowercase = 1,
141         Uppercase = 2,
142         SmallCaps = 3,
143         LineOrParagraphSeparator = 4,
144         Space = 5,
145         SpaceTabOrObject = Space,
146         Nbsp = 6,
147         Tab = 7,
148         TabOrObject = Tab,
149         Object = 8
150     };
151     enum BidiFlags {
152         BidiBN = 1,
153         BidiMaybeResetToParagraphLevel = 2,
154         BidiResetToParagraphLevel = 4,
155         BidiMirrored = 8
156     };
157     unsigned short script    : 8;
158     unsigned short flags     : 4;
159     unsigned short bidiFlags : 4;
160     unsigned short bidiLevel : 8;  // Unicode Bidi algorithm embedding level (0-125)
161     QChar::Direction bidiDirection : 8; // used when running the bidi algorithm
162     inline bool operator == (const QScriptAnalysis &other) const {
163         return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags;
164     }
165 };
166 Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE);
167 
168 struct QGlyphJustification
169 {
QGlyphJustificationQGlyphJustification170     inline QGlyphJustification()
171         : type(0), nKashidas(0), space_18d6(0)
172     {}
173 
174     enum JustificationType {
175         JustifyNone,
176         JustifySpace,
177         JustifyKashida
178     };
179 
180     uint type :2;
181     uint nKashidas : 6; // more do not make sense...
182     uint space_18d6 : 24;
183 };
184 Q_DECLARE_TYPEINFO(QGlyphJustification, Q_PRIMITIVE_TYPE);
185 
186 struct QGlyphAttributes {
187     uchar clusterStart  : 1;
188     uchar dontPrint     : 1;
189     uchar justification : 4;
190     uchar reserved      : 2;
191 };
192 Q_STATIC_ASSERT(sizeof(QGlyphAttributes) == 1);
193 Q_DECLARE_TYPEINFO(QGlyphAttributes, Q_PRIMITIVE_TYPE);
194 
195 struct QGlyphLayout
196 {
197     enum {
198         SpaceNeeded = sizeof(glyph_t) + sizeof(QFixed) + sizeof(QFixedPoint)
199                     + sizeof(QGlyphAttributes) + sizeof(QGlyphJustification)
200     };
201 
202     // init to 0 not needed, done when shaping
203     QFixedPoint *offsets; // 8 bytes per element
204     glyph_t *glyphs; // 4 bytes per element
205     QFixed *advances; // 4 bytes per element
206     QGlyphJustification *justifications; // 4 bytes per element
207     QGlyphAttributes *attributes; // 1 byte per element
208 
209     int numGlyphs;
210 
QGlyphLayoutQGlyphLayout211     inline QGlyphLayout() : numGlyphs(0) {}
212 
QGlyphLayoutQGlyphLayout213     inline explicit QGlyphLayout(char *address, int totalGlyphs)
214     {
215         offsets = reinterpret_cast<QFixedPoint *>(address);
216         int offset = totalGlyphs * sizeof(QFixedPoint);
217         glyphs = reinterpret_cast<glyph_t *>(address + offset);
218         offset += totalGlyphs * sizeof(glyph_t);
219         advances = reinterpret_cast<QFixed *>(address + offset);
220         offset += totalGlyphs * sizeof(QFixed);
221         justifications = reinterpret_cast<QGlyphJustification *>(address + offset);
222         offset += totalGlyphs * sizeof(QGlyphJustification);
223         attributes = reinterpret_cast<QGlyphAttributes *>(address + offset);
224         numGlyphs = totalGlyphs;
225     }
226 
227     inline QGlyphLayout mid(int position, int n = -1) const {
228         QGlyphLayout copy = *this;
229         copy.glyphs += position;
230         copy.advances += position;
231         copy.offsets += position;
232         copy.justifications += position;
233         copy.attributes += position;
234         if (n == -1)
235             copy.numGlyphs -= position;
236         else
237             copy.numGlyphs = n;
238         return copy;
239     }
240 
effectiveAdvanceQGlyphLayout241     inline QFixed effectiveAdvance(int item) const
242     { return (advances[item] + QFixed::fromFixed(justifications[item].space_18d6)) * !attributes[item].dontPrint; }
243 
244     inline void clear(int first = 0, int last = -1) {
245         if (last == -1)
246             last = numGlyphs;
247         if (first == 0 && last == numGlyphs
248             && reinterpret_cast<char *>(offsets + numGlyphs) == reinterpret_cast<char *>(glyphs)) {
249             memset(static_cast<void *>(offsets), 0, (numGlyphs * SpaceNeeded));
250         } else {
251             const int num = last - first;
252             memset(static_cast<void *>(offsets + first), 0, num * sizeof(QFixedPoint));
253             memset(glyphs + first, 0, num * sizeof(glyph_t));
254             memset(static_cast<void *>(advances + first), 0, num * sizeof(QFixed));
255             memset(static_cast<void *>(justifications + first), 0, num * sizeof(QGlyphJustification));
256             memset(attributes + first, 0, num * sizeof(QGlyphAttributes));
257         }
258     }
259 
dataQGlyphLayout260     inline char *data() {
261         return reinterpret_cast<char *>(offsets);
262     }
263 
264     void grow(char *address, int totalGlyphs);
265 };
266 
267 class QVarLengthGlyphLayoutArray : private QVarLengthArray<void *>, public QGlyphLayout
268 {
269 private:
270     typedef QVarLengthArray<void *> Array;
271 public:
QVarLengthGlyphLayoutArray(int totalGlyphs)272     QVarLengthGlyphLayoutArray(int totalGlyphs)
273         : Array((totalGlyphs * SpaceNeeded) / sizeof(void *) + 1)
274         , QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs)
275     {
276         memset(Array::data(), 0, Array::size() * sizeof(void *));
277     }
278 
resize(int totalGlyphs)279     void resize(int totalGlyphs)
280     {
281         Array::resize((totalGlyphs * SpaceNeeded) / sizeof(void *) + 1);
282 
283         *((QGlyphLayout *)this) = QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs);
284         memset(Array::data(), 0, Array::size() * sizeof(void *));
285     }
286 };
287 
288 template <int N> struct QGlyphLayoutArray : public QGlyphLayout
289 {
290 public:
QGlyphLayoutArrayQGlyphLayoutArray291     QGlyphLayoutArray()
292         : QGlyphLayout(reinterpret_cast<char *>(buffer), N)
293     {
294         memset(buffer, 0, sizeof(buffer));
295     }
296 
297 private:
298     void *buffer[(N * SpaceNeeded) / sizeof(void *) + 1];
299 };
300 
301 struct QScriptItem;
302 /// Internal QTextItem
303 class QTextItemInt : public QTextItem
304 {
305 public:
306     inline QTextItemInt() = default;
307     QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format = QTextCharFormat());
308     QTextItemInt(const QGlyphLayout &g, QFont *font, const QChar *chars, int numChars, QFontEngine *fe,
309                  const QTextCharFormat &format = QTextCharFormat());
310 
311     /// copy the structure items, adjusting the glyphs arrays to the right subarrays.
312     /// the width of the returned QTextItemInt is not adjusted, for speed reasons
313     QTextItemInt midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const;
314     void initWithScriptItem(const QScriptItem &si);
315 
316     QFixed descent;
317     QFixed ascent;
318     QFixed width;
319 
320     RenderFlags flags;
321     bool justified = false;
322     QTextCharFormat::UnderlineStyle underlineStyle = QTextCharFormat::NoUnderline;
323     const QTextCharFormat charFormat;
324     int num_chars = 0;
325     const QChar *chars = nullptr;
326     const unsigned short *logClusters = nullptr;
327     const QFont *f = nullptr;
328 
329     QGlyphLayout glyphs;
330     QFontEngine *fontEngine = nullptr;
331 };
332 
333 struct QScriptItem
334 {
QScriptItemQScriptItem335     Q_DECL_CONSTEXPR QScriptItem(int p, QScriptAnalysis a) noexcept
336         : position(p), analysis(a),
337           num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1),
338           glyph_data_offset(0) {}
339 
340     int position;
341     QScriptAnalysis analysis;
342     unsigned short num_glyphs;
343     QFixed descent;
344     QFixed ascent;
345     QFixed leading;
346     QFixed width;
347     int glyph_data_offset;
heightQScriptItem348     Q_DECL_CONSTEXPR QFixed height() const noexcept { return ascent + descent; }
349 private:
350     friend class QVector<QScriptItem>;
QScriptItemQScriptItem351     QScriptItem() {} // for QVector, don't use
352 };
353 Q_DECLARE_TYPEINFO(QScriptItem, Q_PRIMITIVE_TYPE);
354 
355 typedef QVector<QScriptItem> QScriptItemArray;
356 
357 struct Q_AUTOTEST_EXPORT QScriptLine
358 {
359     // created and filled in QTextLine::layout_helper
QScriptLineQScriptLine360     QScriptLine()
361         : from(0), trailingSpaces(0), length(0),
362         justified(0), gridfitted(0),
363         hasTrailingSpaces(0), leadingIncluded(0) {}
364     QFixed descent;
365     QFixed ascent;
366     QFixed leading;
367     QFixed x;
368     QFixed y;
369     QFixed width;
370     QFixed textWidth;
371     QFixed textAdvance;
372     int from;
373     unsigned short trailingSpaces;
374     signed int length : 28;
375     mutable uint justified : 1;
376     mutable uint gridfitted : 1;
377     uint hasTrailingSpaces : 1;
378     uint leadingIncluded : 1;
heightQScriptLine379     QFixed height() const { return ascent + descent
380                             + (leadingIncluded?  qMax(QFixed(),leading) : QFixed()); }
baseQScriptLine381     QFixed base() const { return ascent; }
382     void setDefaultHeight(QTextEngine *eng);
383     void operator+=(const QScriptLine &other);
384 };
385 Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE);
386 
387 
388 inline void QScriptLine::operator+=(const QScriptLine &other)
389 {
390     leading= qMax(leading + ascent, other.leading + other.ascent) - qMax(ascent, other.ascent);
391     descent = qMax(descent, other.descent);
392     ascent = qMax(ascent, other.ascent);
393     textWidth += other.textWidth;
394     length += other.length;
395 }
396 
397 typedef QVector<QScriptLine> QScriptLineArray;
398 
399 class QFontPrivate;
400 class QTextFormatCollection;
401 
402 class Q_GUI_EXPORT QTextEngine {
403 public:
404     enum LayoutState {
405         LayoutEmpty,
406         InLayout,
407         LayoutFailed
408     };
409     struct Q_GUI_EXPORT LayoutData {
410         LayoutData(const QString &str, void **stack_memory, int mem_size);
411         LayoutData();
412         ~LayoutData();
413         mutable QScriptItemArray items;
414         int allocated;
415         int available_glyphs;
416         void **memory;
417         unsigned short *logClustersPtr;
418         QGlyphLayout glyphLayout;
419         mutable int used;
420         uint hasBidi : 1;
421         uint layoutState : 2;
422         uint memory_on_stack : 1;
423         uint haveCharAttributes : 1;
424         QString string;
425         bool reallocate(int totalGlyphs);
426     };
427 
428     struct ItemDecoration {
ItemDecorationItemDecoration429         ItemDecoration() {} // for QVector, don't use
ItemDecorationItemDecoration430         ItemDecoration(qreal x1, qreal x2, qreal y, const QPen &pen):
431             x1(x1), x2(x2), y(y), pen(pen) {}
432 
433         qreal x1;
434         qreal x2;
435         qreal y;
436         QPen pen;
437     };
438 
439     typedef QVector<ItemDecoration> ItemDecorationList;
440 
441     QTextEngine();
442     QTextEngine(const QString &str, const QFont &f);
443     ~QTextEngine();
444 
445     enum Mode {
446         WidthOnly = 0x07
447     };
448 
449     void invalidate();
450     void clearLineData();
451 
452     void validate() const;
453     void itemize() const;
454 
455     bool isRightToLeft() const;
456     static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
457 
458     const QCharAttributes *attributes() const;
459 
460     void shape(int item) const;
461 
462     void justify(const QScriptLine &si);
463     QFixed alignLine(const QScriptLine &line);
464 
465     QFixed width(int charFrom, int numChars) const;
466     glyph_metrics_t boundingBox(int from,  int len) const;
467     glyph_metrics_t tightBoundingBox(int from,  int len) const;
468 
length(int item)469     int length(int item) const {
470         const QScriptItem &si = layoutData->items[item];
471         int from = si.position;
472         item++;
473         return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from;
474     }
length(const QScriptItem * si)475     int length(const QScriptItem *si) const {
476         int end;
477         if (si + 1 < layoutData->items.constData()+ layoutData->items.size())
478             end = (si+1)->position;
479         else
480             end = layoutData->string.length();
481         return end - si->position;
482     }
483 
484     QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = nullptr, QFixed *descent = nullptr, QFixed *leading = nullptr) const;
485     QFont font(const QScriptItem &si) const;
font()486     inline QFont font() const { return fnt; }
487 
488     /**
489      * Returns a pointer to an array of log clusters, offset at the script item.
490      * Each item in the array is a unsigned short.  For each character in the original string there is an entry in the table
491      * so there is a one to one correlation in indexes between the original text and the index in the logcluster.
492      * The value of each item is the position in the glyphs array. Multiple similar pointers in the logclusters array imply
493      * that one glyph is used for more than one character.
494      * \sa glyphs()
495      */
logClusters(const QScriptItem * si)496     inline unsigned short *logClusters(const QScriptItem *si) const
497         { return layoutData->logClustersPtr+si->position; }
498     /**
499      * Returns an array of QGlyphLayout items, offset at the script item.
500      * Each item in the array matches one glyph in the text, storing the advance, position etc.
501      * The returned item's length equals to the number of available glyphs. This may be more
502      * than what was actually shaped.
503      * \sa logClusters()
504      */
availableGlyphs(const QScriptItem * si)505     inline QGlyphLayout availableGlyphs(const QScriptItem *si) const {
506         return layoutData->glyphLayout.mid(si->glyph_data_offset);
507     }
508     /**
509      * Returns an array of QGlyphLayout items, offset at the script item.
510      * Each item in the array matches one glyph in the text, storing the advance, position etc.
511      * The returned item's length equals to the number of shaped glyphs.
512      * \sa logClusters()
513      */
shapedGlyphs(const QScriptItem * si)514     inline QGlyphLayout shapedGlyphs(const QScriptItem *si) const {
515         return layoutData->glyphLayout.mid(si->glyph_data_offset, si->num_glyphs);
516     }
517 
ensureSpace(int nGlyphs)518     inline bool ensureSpace(int nGlyphs) const {
519         if (layoutData->glyphLayout.numGlyphs - layoutData->used < nGlyphs)
520             return layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4);
521         return true;
522     }
523 
524     void freeMemory();
525 
526     int findItem(int strPos, int firstItem = 0) const;
formatCollection()527     inline QTextFormatCollection *formatCollection() const {
528         if (block.docHandle())
529             return block.docHandle()->formatCollection();
530         return specialData ? specialData->formatCollection.data() : nullptr;
531     }
532     QTextCharFormat format(const QScriptItem *si) const;
docLayout()533     inline QAbstractTextDocumentLayout *docLayout() const {
534         Q_ASSERT(block.docHandle());
535         return block.docHandle()->document()->documentLayout();
536     }
537     int formatIndex(const QScriptItem *si) const;
538 
539     /// returns the width of tab at index (in the tabs array) with the tab-start at position x
540     QFixed calculateTabWidth(int index, QFixed x) const;
541 
542     mutable QScriptLineArray lines;
543 
544 private:
545     struct FontEngineCache {
546         FontEngineCache();
547         mutable QFontEngine *prevFontEngine;
548         mutable QFontEngine *prevScaledFontEngine;
549         mutable int prevScript;
550         mutable int prevPosition;
551         mutable int prevLength;
resetFontEngineCache552         inline void reset() {
553             prevFontEngine = nullptr;
554             prevScaledFontEngine = nullptr;
555             prevScript = -1;
556             prevPosition = -1;
557             prevLength = -1;
558         }
559     };
560     mutable FontEngineCache feCache;
561 
562 public:
563     QString text;
564     mutable QFont fnt;
565 #ifndef QT_NO_RAWFONT
566     QRawFont rawFont;
567 #endif
568     QTextBlock block;
569 
570     QTextOption option;
571 
572     QFixed minWidth;
573     QFixed maxWidth;
574     QPointF position;
575     uint ignoreBidi : 1;
576     uint cacheGlyphs : 1;
577     uint stackEngine : 1;
578     uint forceJustification : 1;
579     uint visualMovement : 1;
580     uint delayDecorations: 1;
581 #ifndef QT_NO_RAWFONT
582     uint useRawFont : 1;
583 #endif
584 
585     mutable LayoutData *layoutData;
586 
587     ItemDecorationList underlineList;
588     ItemDecorationList strikeOutList;
589     ItemDecorationList overlineList;
590 
visualCursorMovement()591     inline bool visualCursorMovement() const
592     { return visualMovement || (block.docHandle() && block.docHandle()->defaultCursorMoveStyle == Qt::VisualMoveStyle); }
593 
preeditAreaPosition()594     inline int preeditAreaPosition() const { return specialData ? specialData->preeditPosition : -1; }
preeditAreaText()595     inline QString preeditAreaText() const { return specialData ? specialData->preeditText : QString(); }
596     void setPreeditArea(int position, const QString &text);
597 
hasFormats()598     inline bool hasFormats() const
599     { return block.docHandle() || (specialData && !specialData->formats.isEmpty()); }
formats()600     inline QVector<QTextLayout::FormatRange> formats() const
601     { return specialData ? specialData->formats : QVector<QTextLayout::FormatRange>(); }
602     void setFormats(const QVector<QTextLayout::FormatRange> &formats);
603 
604 private:
605     static void init(QTextEngine *e);
606 
607     struct SpecialData {
608         int preeditPosition;
609         QString preeditText;
610         QVector<QTextLayout::FormatRange> formats;
611         QVector<QTextCharFormat> resolvedFormats;
612         // only used when no docHandle is available
613         QScopedPointer<QTextFormatCollection> formatCollection;
614     };
615     SpecialData *specialData;
616 
617     void indexFormats();
618     void resolveFormats() const;
619 
620 public:
621     bool atWordSeparator(int position) const;
622 
623     QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0, int from = 0, int count = -1) const;
624 
625     void shapeLine(const QScriptLine &line);
626     QFixed leadingSpaceWidth(const QScriptLine &line);
627 
628     QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
629     int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter);
630     int previousLogicalPosition(int oldPos) const;
631     int nextLogicalPosition(int oldPos) const;
632     int lineNumberForTextPosition(int pos);
633     int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
634     std::vector<int> insertionPointsForLine(int lineNum);
635     void resetFontEngineCache();
636 
637     void enableDelayDecorations(bool enable = true) { delayDecorations = enable; }
638 
639     void addUnderline(QPainter *painter, const QLineF &line);
640     void addStrikeOut(QPainter *painter, const QLineF &line);
641     void addOverline(QPainter *painter, const QLineF &line);
642 
643     void drawDecorations(QPainter *painter);
644     void clearDecorations();
645     void adjustUnderlines();
646 
647 private:
648     void addItemDecoration(QPainter *painter, const QLineF &line, ItemDecorationList *decorationList);
649     void adjustUnderlines(ItemDecorationList::iterator start,
650                           ItemDecorationList::iterator end,
651                           qreal underlinePos, qreal penWidth);
652     void drawItemDecorationList(QPainter *painter, const ItemDecorationList &decorationList);
653     void setBoundary(int strPos) const;
654     void addRequiredBoundaries() const;
655     void shapeText(int item) const;
656 #if QT_CONFIG(harfbuzz)
657     int shapeTextWithHarfbuzzNG(const QScriptItem &si,
658                                 const ushort *string,
659                                 int itemLength,
660                                 QFontEngine *fontEngine,
661                                 const QVector<uint> &itemBoundaries,
662                                 bool kerningEnabled,
663                                 bool hasLetterSpacing) const;
664 #endif
665     int shapeTextWithHarfbuzz(const QScriptItem &si, const ushort *string, int itemLength, QFontEngine *fontEngine, const QVector<uint> &itemBoundaries, bool kerningEnabled) const;
666 
667     int endOfLine(int lineNum);
668     int beginningOfLine(int lineNum);
669     int getClusterLength(unsigned short *logClusters, const QCharAttributes *attributes, int from, int to, int glyph_pos, int *start);
670 };
671 
672 class Q_GUI_EXPORT QStackTextEngine : public QTextEngine {
673 public:
674     enum { MemSize = 256*40/sizeof(void *) };
675     QStackTextEngine(const QString &string, const QFont &f);
676     LayoutData _layoutData;
677     void *_memory[MemSize];
678 };
679 Q_DECLARE_TYPEINFO(QTextEngine::ItemDecoration, Q_MOVABLE_TYPE);
680 
681 struct QTextLineItemIterator
682 {
683     QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
684                           const QTextLayout::FormatRange *_selection = nullptr);
685 
atEndQTextLineItemIterator686     inline bool atEnd() const { return logicalItem >= nItems - 1; }
atBeginningQTextLineItemIterator687     inline bool atBeginning() const { return logicalItem <= 0; }
688     QScriptItem &next();
689 
690     bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
isOutsideSelectionQTextLineItemIterator691     inline bool isOutsideSelection() const {
692         QFixed tmp1, tmp2;
693         return !getSelectionBounds(&tmp1, &tmp2);
694     }
695 
696     QTextEngine *eng;
697 
698     QFixed x;
699     const QScriptLine &line;
700     QScriptItem *si;
701 
702     const int lineNum;
703     const int lineEnd;
704     const int firstItem;
705     const int lastItem;
706     const int nItems;
707     int logicalItem;
708     int item;
709     int itemLength;
710 
711     int glyphsStart;
712     int glyphsEnd;
713     int itemStart;
714     int itemEnd;
715 
716     QFixed itemWidth;
717 
718     QVarLengthArray<int> visualOrder;
719 
720     const QTextLayout::FormatRange *selection;
721 };
722 
723 QT_END_NAMESPACE
724 
725 #endif // QTEXTENGINE_P_H
726