src/gui/text/qtextengine.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qdebug.h"
       
    43 #include "qtextformat.h"
       
    44 #include "qtextformat_p.h"
       
    45 #include "qtextengine_p.h"
       
    46 #include "qabstracttextdocumentlayout.h"
       
    47 #include "qtextlayout.h"
       
    48 #include "qtextboundaryfinder.h"
       
    49 #include "qvarlengtharray.h"
       
    50 #include "qfont.h"
       
    51 #include "qfont_p.h"
       
    52 #include "qfontengine_p.h"
       
    53 #include "qstring.h"
       
    54 #include <private/qunicodetables_p.h>
       
    55 #include "qtextdocument_p.h"
       
    56 #include <qapplication.h>
       
    57 #include <stdlib.h>
       
    58 
       
    59 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 namespace {
       
    63 // Helper class used in QTextEngine::itemize
       
    64 // keep it out here to allow us to keep supporting various compilers.
       
    65 class Itemizer {
       
    66 public:
       
    67     Itemizer(const QString &string, const QScriptAnalysis *analysis, QScriptItemArray &items)
       
    68         : m_string(string),
       
    69         m_analysis(analysis),
       
    70         m_items(items),
       
    71         m_splitter(0)
       
    72     {
       
    73     }
       
    74     ~Itemizer()
       
    75     {
       
    76         delete m_splitter;
       
    77     }
       
    78 
       
    79     /// generate the script items
       
    80     /// The caps parameter is used to choose the algoritm of splitting text and assiging roles to the textitems
       
    81     void generate(int start, int length, QFont::Capitalization caps)
       
    82     {
       
    83         if ((int)caps == (int)QFont::SmallCaps)
       
    84             generateScriptItemsSmallCaps(m_string.utf16(), start, length);
       
    85         else if(caps == QFont::Capitalize)
       
    86             generateScriptItemsCapitalize(start, length);
       
    87         else if(caps != QFont::MixedCase) {
       
    88             generateScriptItemsAndChangeCase(start, length,
       
    89                 caps == QFont::AllLowercase ? QScriptAnalysis::Lowercase : QScriptAnalysis::Uppercase);
       
    90         }
       
    91         else
       
    92             generateScriptItems(start, length);
       
    93     }
       
    94 
       
    95 private:
       
    96     enum { MaxItemLength = 4096 };
       
    97 
       
    98     void generateScriptItemsAndChangeCase(int start, int length, QScriptAnalysis::Flags flags)
       
    99     {
       
   100         generateScriptItems(start, length);
       
   101         if (m_items.isEmpty()) // the next loop won't work in that case
       
   102             return;
       
   103         QScriptItemArray::Iterator iter = m_items.end();
       
   104         do {
       
   105             iter--;
       
   106             if (iter->analysis.flags < QScriptAnalysis::TabOrObject)
       
   107                 iter->analysis.flags = flags;
       
   108         } while (iter->position > start);
       
   109     }
       
   110 
       
   111     void generateScriptItems(int start, int length)
       
   112     {
       
   113         if (!length)
       
   114             return;
       
   115         const int end = start + length;
       
   116         for (int i = start + 1; i < end; ++i) {
       
   117             if ((m_analysis[i] == m_analysis[start])
       
   118                 && m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
       
   119                 && i - start < MaxItemLength)
       
   120                 continue;
       
   121             m_items.append(QScriptItem(start, m_analysis[start]));
       
   122             start = i;
       
   123         }
       
   124         m_items.append(QScriptItem(start, m_analysis[start]));
       
   125     }
       
   126 
       
   127     void generateScriptItemsCapitalize(int start, int length)
       
   128     {
       
   129         if (!length)
       
   130             return;
       
   131 
       
   132         if (!m_splitter)
       
   133             m_splitter = new QTextBoundaryFinder(QTextBoundaryFinder::Word,
       
   134                                                  m_string.constData(), m_string.length(),
       
   135                                                  /*buffer*/0, /*buffer size*/0);
       
   136 
       
   137         m_splitter->setPosition(start);
       
   138         QScriptAnalysis itemAnalysis = m_analysis[start];
       
   139 
       
   140         if (m_splitter->boundaryReasons() & QTextBoundaryFinder::StartWord) {
       
   141             itemAnalysis.flags = QScriptAnalysis::Uppercase;
       
   142             m_splitter->toNextBoundary();
       
   143         }
       
   144 
       
   145         const int end = start + length;
       
   146         for (int i = start + 1; i < end; ++i) {
       
   147 
       
   148             bool atWordBoundary = false;
       
   149 
       
   150             if (i == m_splitter->position()) {
       
   151                 if (m_splitter->boundaryReasons() & QTextBoundaryFinder::StartWord
       
   152                     && m_analysis[i].flags < QScriptAnalysis::TabOrObject)
       
   153                     atWordBoundary = true;
       
   154 
       
   155                 m_splitter->toNextBoundary();
       
   156             }
       
   157 
       
   158             if (m_analysis[i] == itemAnalysis
       
   159                 && m_analysis[i].flags < QScriptAnalysis::TabOrObject
       
   160                 && !atWordBoundary
       
   161                 && i - start < MaxItemLength)
       
   162                 continue;
       
   163 
       
   164             m_items.append(QScriptItem(start, itemAnalysis));
       
   165             start = i;
       
   166             itemAnalysis = m_analysis[start];
       
   167 
       
   168             if (atWordBoundary)
       
   169                 itemAnalysis.flags = QScriptAnalysis::Uppercase;
       
   170         }
       
   171         m_items.append(QScriptItem(start, itemAnalysis));
       
   172     }
       
   173 
       
   174     void generateScriptItemsSmallCaps(const ushort *uc, int start, int length)
       
   175     {
       
   176         if (!length)
       
   177             return;
       
   178         bool lower = (QChar::category(uc[start]) == QChar::Letter_Lowercase);
       
   179         const int end = start + length;
       
   180         // split text into parts that are already uppercase and parts that are lowercase, and mark the latter to be uppercased later.
       
   181         for (int i = start + 1; i < end; ++i) {
       
   182             bool l = (QChar::category(uc[i]) == QChar::Letter_Lowercase);
       
   183             if ((m_analysis[i] == m_analysis[start])
       
   184                 && m_analysis[i].flags < QScriptAnalysis::TabOrObject
       
   185                 && l == lower
       
   186                 && i - start < MaxItemLength)
       
   187                 continue;
       
   188             m_items.append(QScriptItem(start, m_analysis[start]));
       
   189             if (lower)
       
   190                 m_items.last().analysis.flags = QScriptAnalysis::SmallCaps;
       
   191 
       
   192             start = i;
       
   193             lower = l;
       
   194         }
       
   195         m_items.append(QScriptItem(start, m_analysis[start]));
       
   196         if (lower)
       
   197             m_items.last().analysis.flags = QScriptAnalysis::SmallCaps;
       
   198     }
       
   199 
       
   200     const QString &m_string;
       
   201     const QScriptAnalysis * const m_analysis;
       
   202     QScriptItemArray &m_items;
       
   203     QTextBoundaryFinder *m_splitter;
       
   204 };
       
   205 }
       
   206 
       
   207 
       
   208 // ----------------------------------------------------------------------------
       
   209 //
       
   210 // The BiDi algorithm
       
   211 //
       
   212 // ----------------------------------------------------------------------------
       
   213 
       
   214 #define BIDI_DEBUG 0
       
   215 #if (BIDI_DEBUG >= 1)
       
   216 QT_BEGIN_INCLUDE_NAMESPACE
       
   217 #include <iostream>
       
   218 QT_END_INCLUDE_NAMESPACE
       
   219 using namespace std;
       
   220 
       
   221 static const char *directions[] = {
       
   222     "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
       
   223     "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN"
       
   224 };
       
   225 
       
   226 #endif
       
   227 
       
   228 struct QBidiStatus {
       
   229     QBidiStatus() {
       
   230         eor = QChar::DirON;
       
   231         lastStrong = QChar::DirON;
       
   232         last = QChar:: DirON;
       
   233         dir = QChar::DirON;
       
   234     }
       
   235     QChar::Direction eor;
       
   236     QChar::Direction lastStrong;
       
   237     QChar::Direction last;
       
   238     QChar::Direction dir;
       
   239 };
       
   240 
       
   241 enum { MaxBidiLevel = 61 };
       
   242 
       
   243 struct QBidiControl {
       
   244     inline QBidiControl(bool rtl)
       
   245         : cCtx(0), base(rtl ? 1 : 0), level(rtl ? 1 : 0), override(false) {}
       
   246 
       
   247     inline void embed(bool rtl, bool o = false) {
       
   248         unsigned int toAdd = 1;
       
   249         if((level%2 != 0) == rtl ) {
       
   250             ++toAdd;
       
   251         }
       
   252         if (level + toAdd <= MaxBidiLevel) {
       
   253             ctx[cCtx].level = level;
       
   254             ctx[cCtx].override = override;
       
   255             cCtx++;
       
   256             override = o;
       
   257             level += toAdd;
       
   258         }
       
   259     }
       
   260     inline bool canPop() const { return cCtx != 0; }
       
   261     inline void pdf() {
       
   262         Q_ASSERT(cCtx);
       
   263         --cCtx;
       
   264         level = ctx[cCtx].level;
       
   265         override = ctx[cCtx].override;
       
   266     }
       
   267 
       
   268     inline QChar::Direction basicDirection() const {
       
   269         return (base ? QChar::DirR : QChar:: DirL);
       
   270     }
       
   271     inline unsigned int baseLevel() const {
       
   272         return base;
       
   273     }
       
   274     inline QChar::Direction direction() const {
       
   275         return ((level%2) ? QChar::DirR : QChar:: DirL);
       
   276     }
       
   277 
       
   278     struct {
       
   279         unsigned int level;
       
   280         bool override;
       
   281     } ctx[MaxBidiLevel];
       
   282     unsigned int cCtx;
       
   283     const unsigned int base;
       
   284     unsigned int level;
       
   285     bool override;
       
   286 };
       
   287 
       
   288 
       
   289 static void appendItems(QScriptAnalysis *analysis, int &start, int &stop, const QBidiControl &control, QChar::Direction dir)
       
   290 {
       
   291     if (start > stop)
       
   292         return;
       
   293 
       
   294     int level = control.level;
       
   295 
       
   296     if(dir != QChar::DirON && !control.override) {
       
   297         // add level of run (cases I1 & I2)
       
   298         if(level % 2) {
       
   299             if(dir == QChar::DirL || dir == QChar::DirAN || dir == QChar::DirEN)
       
   300                 level++;
       
   301         } else {
       
   302             if(dir == QChar::DirR)
       
   303                 level++;
       
   304             else if(dir == QChar::DirAN || dir == QChar::DirEN)
       
   305                 level += 2;
       
   306         }
       
   307     }
       
   308 
       
   309 #if (BIDI_DEBUG >= 1)
       
   310     qDebug("new run: dir=%s from %d, to %d level = %d override=%d", directions[dir], start, stop, level, control.override);
       
   311 #endif
       
   312     QScriptAnalysis *s = analysis + start;
       
   313     const QScriptAnalysis *e = analysis + stop;
       
   314     while (s <= e) {
       
   315         s->bidiLevel = level;
       
   316         ++s;
       
   317     }
       
   318     ++stop;
       
   319     start = stop;
       
   320 }
       
   321 
       
   322 
       
   323 // creates the next QScript items.
       
   324 static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiControl &control)
       
   325 {
       
   326     bool rightToLeft = (control.basicDirection() == 1);
       
   327     bool hasBidi = rightToLeft;
       
   328 #if BIDI_DEBUG >= 2
       
   329     qDebug() << "bidiItemize: rightToLeft=" << rightToLeft << engine->layoutData->string;
       
   330 #endif
       
   331 
       
   332     int sor = 0;
       
   333     int eor = -1;
       
   334 
       
   335 
       
   336     int length = engine->layoutData->string.length();
       
   337 
       
   338     const ushort *unicode = (const ushort *)engine->layoutData->string.unicode();
       
   339     int current = 0;
       
   340 
       
   341     QChar::Direction dir = rightToLeft ? QChar::DirR : QChar::DirL;
       
   342     QBidiStatus status;
       
   343 
       
   344     QChar::Direction sdir = QChar::direction(*unicode);
       
   345     if (sdir != QChar::DirL && sdir != QChar::DirR && sdir != QChar::DirEN && sdir != QChar::DirAN)
       
   346         sdir = QChar::DirON;
       
   347     else
       
   348         dir = QChar::DirON;
       
   349     status.eor = sdir;
       
   350     status.lastStrong = rightToLeft ? QChar::DirR : QChar::DirL;
       
   351     status.last = status.lastStrong;
       
   352     status.dir = sdir;
       
   353 
       
   354 
       
   355     while (current <= length) {
       
   356 
       
   357         QChar::Direction dirCurrent;
       
   358         if (current == (int)length)
       
   359             dirCurrent = control.basicDirection();
       
   360         else
       
   361             dirCurrent = QChar::direction(unicode[current]);
       
   362 
       
   363 #if (BIDI_DEBUG >= 2)
       
   364 //         qDebug() << "pos=" << current << " dir=" << directions[dir]
       
   365 //                  << " current=" << directions[dirCurrent] << " last=" << directions[status.last]
       
   366 //                  << " eor=" << eor << '/' << directions[status.eor]
       
   367 //                  << " sor=" << sor << " lastStrong="
       
   368 //                  << directions[status.lastStrong]
       
   369 //                  << " level=" << (int)control.level << " override=" << (bool)control.override;
       
   370 #endif
       
   371 
       
   372         switch(dirCurrent) {
       
   373 
       
   374             // embedding and overrides (X1-X9 in the BiDi specs)
       
   375         case QChar::DirRLE:
       
   376         case QChar::DirRLO:
       
   377         case QChar::DirLRE:
       
   378         case QChar::DirLRO:
       
   379             {
       
   380                 bool rtl = (dirCurrent == QChar::DirRLE || dirCurrent == QChar::DirRLO);
       
   381                 hasBidi |= rtl;
       
   382                 bool override = (dirCurrent == QChar::DirLRO || dirCurrent == QChar::DirRLO);
       
   383 
       
   384                 unsigned int level = control.level+1;
       
   385                 if ((level%2 != 0) == rtl) ++level;
       
   386                 if(level < MaxBidiLevel) {
       
   387                     eor = current-1;
       
   388                     appendItems(analysis, sor, eor, control, dir);
       
   389                     eor = current;
       
   390                     control.embed(rtl, override);
       
   391                     QChar::Direction edir = (rtl ? QChar::DirR : QChar::DirL);
       
   392                     dir = status.eor = edir;
       
   393                     status.lastStrong = edir;
       
   394                 }
       
   395                 break;
       
   396             }
       
   397         case QChar::DirPDF:
       
   398             {
       
   399                 if (control.canPop()) {
       
   400                     if (dir != control.direction()) {
       
   401                         eor = current-1;
       
   402                         appendItems(analysis, sor, eor, control, dir);
       
   403                         dir = control.direction();
       
   404                     }
       
   405                     eor = current;
       
   406                     appendItems(analysis, sor, eor, control, dir);
       
   407                     control.pdf();
       
   408                     dir = QChar::DirON; status.eor = QChar::DirON;
       
   409                     status.last = control.direction();
       
   410                     if (control.override)
       
   411                         dir = control.direction();
       
   412                     else
       
   413                         dir = QChar::DirON;
       
   414                     status.lastStrong = control.direction();
       
   415                 }
       
   416                 break;
       
   417             }
       
   418 
       
   419             // strong types
       
   420         case QChar::DirL:
       
   421             if(dir == QChar::DirON)
       
   422                 dir = QChar::DirL;
       
   423             switch(status.last)
       
   424                 {
       
   425                 case QChar::DirL:
       
   426                     eor = current; status.eor = QChar::DirL; break;
       
   427                 case QChar::DirR:
       
   428                 case QChar::DirAL:
       
   429                 case QChar::DirEN:
       
   430                 case QChar::DirAN:
       
   431                     if (eor >= 0) {
       
   432                         appendItems(analysis, sor, eor, control, dir);
       
   433                         dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
       
   434                         status.eor = dir;
       
   435                     } else {
       
   436                         eor = current; status.eor = dir;
       
   437                     }
       
   438                     break;
       
   439                 case QChar::DirES:
       
   440                 case QChar::DirET:
       
   441                 case QChar::DirCS:
       
   442                 case QChar::DirBN:
       
   443                 case QChar::DirB:
       
   444                 case QChar::DirS:
       
   445                 case QChar::DirWS:
       
   446                 case QChar::DirON:
       
   447                     if(dir != QChar::DirL) {
       
   448                         //last stuff takes embedding dir
       
   449                         if(control.direction() == QChar::DirR) {
       
   450                             if(status.eor != QChar::DirR) {
       
   451                                 // AN or EN
       
   452                                 appendItems(analysis, sor, eor, control, dir);
       
   453                                 status.eor = QChar::DirON;
       
   454                                 dir = QChar::DirR;
       
   455                             }
       
   456                             eor = current - 1;
       
   457                             appendItems(analysis, sor, eor, control, dir);
       
   458                             dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
       
   459                             status.eor = dir;
       
   460                         } else {
       
   461                             if(status.eor != QChar::DirL) {
       
   462                                 appendItems(analysis, sor, eor, control, dir);
       
   463                                 status.eor = QChar::DirON;
       
   464                                 dir = QChar::DirL;
       
   465                             } else {
       
   466                                 eor = current; status.eor = QChar::DirL; break;
       
   467                             }
       
   468                         }
       
   469                     } else {
       
   470                         eor = current; status.eor = QChar::DirL;
       
   471                     }
       
   472                 default:
       
   473                     break;
       
   474                 }
       
   475             status.lastStrong = QChar::DirL;
       
   476             break;
       
   477         case QChar::DirAL:
       
   478         case QChar::DirR:
       
   479             hasBidi = true;
       
   480             if(dir == QChar::DirON) dir = QChar::DirR;
       
   481             switch(status.last)
       
   482                 {
       
   483                 case QChar::DirL:
       
   484                 case QChar::DirEN:
       
   485                 case QChar::DirAN:
       
   486                     if (eor >= 0)
       
   487                         appendItems(analysis, sor, eor, control, dir);
       
   488                     // fall through
       
   489                 case QChar::DirR:
       
   490                 case QChar::DirAL:
       
   491                     dir = QChar::DirR; eor = current; status.eor = QChar::DirR; break;
       
   492                 case QChar::DirES:
       
   493                 case QChar::DirET:
       
   494                 case QChar::DirCS:
       
   495                 case QChar::DirBN:
       
   496                 case QChar::DirB:
       
   497                 case QChar::DirS:
       
   498                 case QChar::DirWS:
       
   499                 case QChar::DirON:
       
   500                     if(status.eor != QChar::DirR && status.eor != QChar::DirAL) {
       
   501                         //last stuff takes embedding dir
       
   502                         if(control.direction() == QChar::DirR
       
   503                            || status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL) {
       
   504                             appendItems(analysis, sor, eor, control, dir);
       
   505                             dir = QChar::DirR; status.eor = QChar::DirON;
       
   506                             eor = current;
       
   507                         } else {
       
   508                             eor = current - 1;
       
   509                             appendItems(analysis, sor, eor, control, dir);
       
   510                             dir = QChar::DirR; status.eor = QChar::DirON;
       
   511                         }
       
   512                     } else {
       
   513                         eor = current; status.eor = QChar::DirR;
       
   514                     }
       
   515                 default:
       
   516                     break;
       
   517                 }
       
   518             status.lastStrong = dirCurrent;
       
   519             break;
       
   520 
       
   521             // weak types:
       
   522 
       
   523         case QChar::DirNSM:
       
   524             if (eor == current-1)
       
   525                 eor = current;
       
   526             break;
       
   527         case QChar::DirEN:
       
   528             // if last strong was AL change EN to AN
       
   529             if(status.lastStrong != QChar::DirAL) {
       
   530                 if(dir == QChar::DirON) {
       
   531                     if(status.lastStrong == QChar::DirL)
       
   532                         dir = QChar::DirL;
       
   533                     else
       
   534                         dir = QChar::DirEN;
       
   535                 }
       
   536                 switch(status.last)
       
   537                     {
       
   538                     case QChar::DirET:
       
   539                         if (status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL) {
       
   540                             appendItems(analysis, sor, eor, control, dir);
       
   541                             status.eor = QChar::DirON;
       
   542                             dir = QChar::DirAN;
       
   543                         }
       
   544                         // fall through
       
   545                     case QChar::DirEN:
       
   546                     case QChar::DirL:
       
   547                         eor = current;
       
   548                         status.eor = dirCurrent;
       
   549                         break;
       
   550                     case QChar::DirR:
       
   551                     case QChar::DirAL:
       
   552                     case QChar::DirAN:
       
   553                         if (eor >= 0)
       
   554                             appendItems(analysis, sor, eor, control, dir);
       
   555                         else
       
   556                             eor = current;
       
   557                         status.eor = QChar::DirEN;
       
   558                         dir = QChar::DirAN; break;
       
   559                     case QChar::DirES:
       
   560                     case QChar::DirCS:
       
   561                         if(status.eor == QChar::DirEN || dir == QChar::DirAN) {
       
   562                             eor = current; break;
       
   563                         }
       
   564                     case QChar::DirBN:
       
   565                     case QChar::DirB:
       
   566                     case QChar::DirS:
       
   567                     case QChar::DirWS:
       
   568                     case QChar::DirON:
       
   569                         if(status.eor == QChar::DirR) {
       
   570                             // neutrals go to R
       
   571                             eor = current - 1;
       
   572                             appendItems(analysis, sor, eor, control, dir);
       
   573                             dir = QChar::DirON; status.eor = QChar::DirEN;
       
   574                             dir = QChar::DirAN;
       
   575                         }
       
   576                         else if(status.eor == QChar::DirL ||
       
   577                                  (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
       
   578                             eor = current; status.eor = dirCurrent;
       
   579                         } else {
       
   580                             // numbers on both sides, neutrals get right to left direction
       
   581                             if(dir != QChar::DirL) {
       
   582                                 appendItems(analysis, sor, eor, control, dir);
       
   583                                 dir = QChar::DirON; status.eor = QChar::DirON;
       
   584                                 eor = current - 1;
       
   585                                 dir = QChar::DirR;
       
   586                                 appendItems(analysis, sor, eor, control, dir);
       
   587                                 dir = QChar::DirON; status.eor = QChar::DirON;
       
   588                                 dir = QChar::DirAN;
       
   589                             } else {
       
   590                                 eor = current; status.eor = dirCurrent;
       
   591                             }
       
   592                         }
       
   593                     default:
       
   594                         break;
       
   595                     }
       
   596                 break;
       
   597             }
       
   598         case QChar::DirAN:
       
   599             hasBidi = true;
       
   600             dirCurrent = QChar::DirAN;
       
   601             if(dir == QChar::DirON) dir = QChar::DirAN;
       
   602             switch(status.last)
       
   603                 {
       
   604                 case QChar::DirL:
       
   605                 case QChar::DirAN:
       
   606                     eor = current; status.eor = QChar::DirAN; break;
       
   607                 case QChar::DirR:
       
   608                 case QChar::DirAL:
       
   609                 case QChar::DirEN:
       
   610                     if (eor >= 0){
       
   611                         appendItems(analysis, sor, eor, control, dir);
       
   612                     } else {
       
   613                         eor = current;
       
   614                     }
       
   615                     dir = QChar::DirON; status.eor = QChar::DirAN;
       
   616                     break;
       
   617                 case QChar::DirCS:
       
   618                     if(status.eor == QChar::DirAN) {
       
   619                         eor = current; break;
       
   620                     }
       
   621                 case QChar::DirES:
       
   622                 case QChar::DirET:
       
   623                 case QChar::DirBN:
       
   624                 case QChar::DirB:
       
   625                 case QChar::DirS:
       
   626                 case QChar::DirWS:
       
   627                 case QChar::DirON:
       
   628                     if(status.eor == QChar::DirR) {
       
   629                         // neutrals go to R
       
   630                         eor = current - 1;
       
   631                         appendItems(analysis, sor, eor, control, dir);
       
   632                         status.eor = QChar::DirAN;
       
   633                         dir = QChar::DirAN;
       
   634                     } else if(status.eor == QChar::DirL ||
       
   635                                (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
       
   636                         eor = current; status.eor = dirCurrent;
       
   637                     } else {
       
   638                         // numbers on both sides, neutrals get right to left direction
       
   639                         if(dir != QChar::DirL) {
       
   640                             appendItems(analysis, sor, eor, control, dir);
       
   641                             status.eor = QChar::DirON;
       
   642                             eor = current - 1;
       
   643                             dir = QChar::DirR;
       
   644                             appendItems(analysis, sor, eor, control, dir);
       
   645                             status.eor = QChar::DirAN;
       
   646                             dir = QChar::DirAN;
       
   647                         } else {
       
   648                             eor = current; status.eor = dirCurrent;
       
   649                         }
       
   650                     }
       
   651                 default:
       
   652                     break;
       
   653                 }
       
   654             break;
       
   655         case QChar::DirES:
       
   656         case QChar::DirCS:
       
   657             break;
       
   658         case QChar::DirET:
       
   659             if(status.last == QChar::DirEN) {
       
   660                 dirCurrent = QChar::DirEN;
       
   661                 eor = current; status.eor = dirCurrent;
       
   662             }
       
   663             break;
       
   664 
       
   665             // boundary neutrals should be ignored
       
   666         case QChar::DirBN:
       
   667             break;
       
   668             // neutrals
       
   669         case QChar::DirB:
       
   670             // ### what do we do with newline and paragraph separators that come to here?
       
   671             break;
       
   672         case QChar::DirS:
       
   673             // ### implement rule L1
       
   674             break;
       
   675         case QChar::DirWS:
       
   676         case QChar::DirON:
       
   677             break;
       
   678         default:
       
   679             break;
       
   680         }
       
   681 
       
   682         //qDebug() << "     after: dir=" << //        dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << control.direction();
       
   683 
       
   684         if(current >= (int)length) break;
       
   685 
       
   686         // set status.last as needed.
       
   687         switch(dirCurrent) {
       
   688         case QChar::DirET:
       
   689         case QChar::DirES:
       
   690         case QChar::DirCS:
       
   691         case QChar::DirS:
       
   692         case QChar::DirWS:
       
   693         case QChar::DirON:
       
   694             switch(status.last)
       
   695             {
       
   696             case QChar::DirL:
       
   697             case QChar::DirR:
       
   698             case QChar::DirAL:
       
   699             case QChar::DirEN:
       
   700             case QChar::DirAN:
       
   701                 status.last = dirCurrent;
       
   702                 break;
       
   703             default:
       
   704                 status.last = QChar::DirON;
       
   705             }
       
   706             break;
       
   707         case QChar::DirNSM:
       
   708         case QChar::DirBN:
       
   709             // ignore these
       
   710             break;
       
   711         case QChar::DirLRO:
       
   712         case QChar::DirLRE:
       
   713             status.last = QChar::DirL;
       
   714             break;
       
   715         case QChar::DirRLO:
       
   716         case QChar::DirRLE:
       
   717             status.last = QChar::DirR;
       
   718             break;
       
   719         case QChar::DirEN:
       
   720             if (status.last == QChar::DirL) {
       
   721                 status.last = QChar::DirL;
       
   722                 break;
       
   723             }
       
   724             // fall through
       
   725         default:
       
   726             status.last = dirCurrent;
       
   727         }
       
   728 
       
   729         ++current;
       
   730     }
       
   731 
       
   732 #if (BIDI_DEBUG >= 1)
       
   733     qDebug() << "reached end of line current=" << current << ", eor=" << eor;
       
   734 #endif
       
   735     eor = current - 1; // remove dummy char
       
   736 
       
   737     if (sor <= eor)
       
   738         appendItems(analysis, sor, eor, control, dir);
       
   739 
       
   740     return hasBidi;
       
   741 }
       
   742 
       
   743 void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrder)
       
   744 {
       
   745 
       
   746     // first find highest and lowest levels
       
   747     quint8 levelLow = 128;
       
   748     quint8 levelHigh = 0;
       
   749     int i = 0;
       
   750     while (i < numItems) {
       
   751         //printf("level = %d\n", r->level);
       
   752         if (levels[i] > levelHigh)
       
   753             levelHigh = levels[i];
       
   754         if (levels[i] < levelLow)
       
   755             levelLow = levels[i];
       
   756         i++;
       
   757     }
       
   758 
       
   759     // implements reordering of the line (L2 according to BiDi spec):
       
   760     // L2. From the highest level found in the text to the lowest odd level on each line,
       
   761     // reverse any contiguous sequence of characters that are at that level or higher.
       
   762 
       
   763     // reversing is only done up to the lowest odd level
       
   764     if(!(levelLow%2)) levelLow++;
       
   765 
       
   766 #if (BIDI_DEBUG >= 1)
       
   767 //     qDebug() << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh;
       
   768 #endif
       
   769 
       
   770     int count = numItems - 1;
       
   771     for (i = 0; i < numItems; i++)
       
   772         visualOrder[i] = i;
       
   773 
       
   774     while(levelHigh >= levelLow) {
       
   775         int i = 0;
       
   776         while (i < count) {
       
   777             while(i < count && levels[i] < levelHigh) i++;
       
   778             int start = i;
       
   779             while(i <= count && levels[i] >= levelHigh) i++;
       
   780             int end = i-1;
       
   781 
       
   782             if(start != end) {
       
   783                 //qDebug() << "reversing from " << start << " to " << end;
       
   784                 for(int j = 0; j < (end-start+1)/2; j++) {
       
   785                     int tmp = visualOrder[start+j];
       
   786                     visualOrder[start+j] = visualOrder[end-j];
       
   787                     visualOrder[end-j] = tmp;
       
   788                 }
       
   789             }
       
   790             i++;
       
   791         }
       
   792         levelHigh--;
       
   793     }
       
   794 
       
   795 #if (BIDI_DEBUG >= 1)
       
   796 //     qDebug() << "visual order is:";
       
   797 //     for (i = 0; i < numItems; i++)
       
   798 //         qDebug() << visualOrder[i];
       
   799 #endif
       
   800 }
       
   801 
       
   802 QT_BEGIN_INCLUDE_NAMESPACE
       
   803 
       
   804 #if defined(Q_WS_X11) || defined (Q_WS_QWS)
       
   805 #   include "qfontengine_ft_p.h"
       
   806 #elif defined(Q_WS_MAC)
       
   807 # include "qtextengine_mac.cpp"
       
   808 #endif
       
   809 
       
   810 #include <private/qharfbuzz_p.h>
       
   811 
       
   812 QT_END_INCLUDE_NAMESPACE
       
   813 
       
   814 // ask the font engine to find out which glyphs (as an index in the specific font) to use for the text in one item.
       
   815 static bool stringToGlyphs(HB_ShaperItem *item, QGlyphLayout *glyphs, QFontEngine *fontEngine)
       
   816 {
       
   817     int nGlyphs = item->num_glyphs;
       
   818 
       
   819     QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
       
   820     if (item->item.bidiLevel % 2)
       
   821         shaperFlags |= QTextEngine::RightToLeft;
       
   822 
       
   823     bool result = fontEngine->stringToCMap(reinterpret_cast<const QChar *>(item->string + item->item.pos), item->item.length, glyphs, &nGlyphs, shaperFlags);
       
   824     item->num_glyphs = nGlyphs;
       
   825     glyphs->numGlyphs = nGlyphs;
       
   826     return result;
       
   827 }
       
   828 
       
   829 // shape all the items that intersect with the line, taking tab widths into account to find out what text actually fits in the line.
       
   830 void QTextEngine::shapeLine(const QScriptLine &line)
       
   831 {
       
   832     QFixed x;
       
   833     bool first = true;
       
   834     const int end = findItem(line.from + line.length - 1);
       
   835     int item = findItem(line.from);
       
   836     if (item == -1)
       
   837         return;
       
   838     for (item = findItem(line.from); item <= end; ++item) {
       
   839         QScriptItem &si = layoutData->items[item];
       
   840         if (si.analysis.flags == QScriptAnalysis::Tab) {
       
   841             ensureSpace(1);
       
   842             si.width = calculateTabWidth(item, x);
       
   843         } else {
       
   844             shape(item);
       
   845         }
       
   846         if (first && si.position != line.from) { // that means our x position has to be offset
       
   847             QGlyphLayout glyphs = shapedGlyphs(&si);
       
   848             Q_ASSERT(line.from > si.position);
       
   849             for (int i = line.from - si.position - 1; i >= 0; i--) {
       
   850                 x -= glyphs.effectiveAdvance(i);
       
   851             }
       
   852         }
       
   853         first = false;
       
   854 
       
   855         x += si.width;
       
   856     }
       
   857 }
       
   858 
       
   859 extern int qt_defaultDpiY(); // in qfont.cpp
       
   860 
       
   861 void QTextEngine::shapeText(int item) const
       
   862 {
       
   863     Q_ASSERT(item < layoutData->items.size());
       
   864     QScriptItem &si = layoutData->items[item];
       
   865 
       
   866     if (si.num_glyphs)
       
   867         return;
       
   868 
       
   869 #if defined(Q_WS_MAC)
       
   870     shapeTextMac(item);
       
   871 #elif defined(Q_WS_WINCE)
       
   872     shapeTextWithCE(item);
       
   873 #else
       
   874     shapeTextWithHarfbuzz(item);
       
   875 #endif
       
   876 
       
   877     si.width = 0;
       
   878 
       
   879     if (!si.num_glyphs)
       
   880         return;
       
   881     QGlyphLayout glyphs = shapedGlyphs(&si);
       
   882 
       
   883     QFont font = this->font(si);
       
   884     bool letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
       
   885     QFixed letterSpacing = font.d->letterSpacing;
       
   886     QFixed wordSpacing = font.d->wordSpacing;
       
   887 
       
   888     if (letterSpacingIsAbsolute)
       
   889         letterSpacing *= font.d->dpi / qt_defaultDpiY();
       
   890 
       
   891     if (letterSpacing != 0) {
       
   892         for (int i = 1; i < si.num_glyphs; ++i) {
       
   893             if (glyphs.attributes[i].clusterStart) {
       
   894                 if (letterSpacingIsAbsolute)
       
   895                     glyphs.advances_x[i-1] += letterSpacing;
       
   896                 else {
       
   897                     const QFixed advance = glyphs.advances_x[i-1];
       
   898                     glyphs.advances_x[i-1] += (letterSpacing - 100) * advance / 100;
       
   899                 }
       
   900             }
       
   901         }
       
   902         if (letterSpacingIsAbsolute)
       
   903             glyphs.advances_x[si.num_glyphs-1] += letterSpacing;
       
   904         else {
       
   905             const QFixed advance = glyphs.advances_x[si.num_glyphs-1];
       
   906             glyphs.advances_x[si.num_glyphs-1] += (letterSpacing - 100) * advance / 100;
       
   907         }
       
   908     }
       
   909     if (wordSpacing != 0) {
       
   910         for (int i = 0; i < si.num_glyphs; ++i) {
       
   911             if (glyphs.attributes[i].justification == HB_Space
       
   912                 || glyphs.attributes[i].justification == HB_Arabic_Space) {
       
   913                 // word spacing only gets added once to a consecutive run of spaces (see CSS spec)
       
   914                 if (i + 1 == si.num_glyphs
       
   915                     ||(glyphs.attributes[i+1].justification != HB_Space
       
   916                        && glyphs.attributes[i+1].justification != HB_Arabic_Space))
       
   917                     glyphs.advances_x[i] += wordSpacing;
       
   918             }
       
   919         }
       
   920     }
       
   921 
       
   922     for (int i = 0; i < si.num_glyphs; ++i)
       
   923         si.width += glyphs.advances_x[i];
       
   924 }
       
   925 
       
   926 #if defined(Q_WS_WINCE) //TODO
       
   927 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
       
   928 // and no reordering.
       
   929 // also computes logClusters heuristically
       
   930 static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayout *glyphs, unsigned short *logClusters, int num_glyphs)
       
   931 {
       
   932     // ### zeroWidth and justification are missing here!!!!!
       
   933 
       
   934     Q_UNUSED(num_glyphs);
       
   935     Q_ASSERT(num_glyphs <= length);
       
   936 
       
   937 //     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
       
   938 
       
   939     int glyph_pos = 0;
       
   940     for (int i = 0; i < length; i++) {
       
   941         if (uc[i].unicode() >= 0xd800 && uc[i].unicode() < 0xdc00 && i < length-1
       
   942             && uc[i+1].unicode() >= 0xdc00 && uc[i+1].unicode() < 0xe000) {
       
   943             logClusters[i] = glyph_pos;
       
   944             logClusters[++i] = glyph_pos;
       
   945         } else {
       
   946             logClusters[i] = glyph_pos;
       
   947         }
       
   948         ++glyph_pos;
       
   949     }
       
   950 
       
   951     // first char in a run is never (treated as) a mark
       
   952     int cStart = 0;
       
   953 
       
   954     const bool symbolFont = false; // ####
       
   955     glyphs->attributes[0].mark = false;
       
   956     glyphs->attributes[0].clusterStart = true;
       
   957     glyphs->attributes[0].dontPrint = (!symbolFont && uc[0].unicode() == 0x00ad) || qIsControlChar(uc[0].unicode());
       
   958 
       
   959     int pos = 0;
       
   960     int lastCat = QChar::category(uc[0].unicode());
       
   961     for (int i = 1; i < length; ++i) {
       
   962         if (logClusters[i] == pos)
       
   963             // same glyph
       
   964             continue;
       
   965         ++pos;
       
   966         while (pos < logClusters[i]) {
       
   967             glyphs[pos].attributes = glyphs[pos-1].attributes;
       
   968             ++pos;
       
   969         }
       
   970         // hide soft-hyphens by default
       
   971         if ((!symbolFont && uc[i].unicode() == 0x00ad) || qIsControlChar(uc[i].unicode()))
       
   972             glyphs->attributes[pos].dontPrint = true;
       
   973         const QUnicodeTables::Properties *prop = QUnicodeTables::properties(uc[i].unicode());
       
   974         int cat = prop->category;
       
   975         if (cat != QChar::Mark_NonSpacing) {
       
   976             glyphs->attributes[pos].mark = false;
       
   977             glyphs->attributes[pos].clusterStart = true;
       
   978             glyphs->attributes[pos].combiningClass = 0;
       
   979             cStart = logClusters[i];
       
   980         } else {
       
   981             int cmb = prop->combiningClass;
       
   982 
       
   983             if (cmb == 0) {
       
   984                 // Fix 0 combining classes
       
   985                 if ((uc[pos].unicode() & 0xff00) == 0x0e00) {
       
   986                     // thai or lao
       
   987                     unsigned char col = uc[pos].cell();
       
   988                     if (col == 0x31 ||
       
   989                          col == 0x34 ||
       
   990                          col == 0x35 ||
       
   991                          col == 0x36 ||
       
   992                          col == 0x37 ||
       
   993                          col == 0x47 ||
       
   994                          col == 0x4c ||
       
   995                          col == 0x4d ||
       
   996                          col == 0x4e) {
       
   997                         cmb = QChar::Combining_AboveRight;
       
   998                     } else if (col == 0xb1 ||
       
   999                                 col == 0xb4 ||
       
  1000                                 col == 0xb5 ||
       
  1001                                 col == 0xb6 ||
       
  1002                                 col == 0xb7 ||
       
  1003                                 col == 0xbb ||
       
  1004                                 col == 0xcc ||
       
  1005                                 col == 0xcd) {
       
  1006                         cmb = QChar::Combining_Above;
       
  1007                     } else if (col == 0xbc) {
       
  1008                         cmb = QChar::Combining_Below;
       
  1009                     }
       
  1010                 }
       
  1011             }
       
  1012 
       
  1013             glyphs->attributes[pos].mark = true;
       
  1014             glyphs->attributes[pos].clusterStart = false;
       
  1015             glyphs->attributes[pos].combiningClass = cmb;
       
  1016             logClusters[i] = cStart;
       
  1017             glyphs->advances_x[pos] = 0;
       
  1018             glyphs->advances_y[pos] = 0;
       
  1019         }
       
  1020 
       
  1021         // one gets an inter character justification point if the current char is not a non spacing mark.
       
  1022         // as then the current char belongs to the last one and one gets a space justification point
       
  1023         // after the space char.
       
  1024         if (lastCat == QChar::Separator_Space)
       
  1025             glyphs->attributes[pos-1].justification = HB_Space;
       
  1026         else if (cat != QChar::Mark_NonSpacing)
       
  1027             glyphs->attributes[pos-1].justification = HB_Character;
       
  1028         else
       
  1029             glyphs->attributes[pos-1].justification = HB_NoJustification;
       
  1030 
       
  1031         lastCat = cat;
       
  1032     }
       
  1033     pos = logClusters[length-1];
       
  1034     if (lastCat == QChar::Separator_Space)
       
  1035         glyphs->attributes[pos].justification = HB_Space;
       
  1036     else
       
  1037         glyphs->attributes[pos].justification = HB_Character;
       
  1038 }
       
  1039 
       
  1040 void QTextEngine::shapeTextWithCE(int item) const
       
  1041 {
       
  1042     QScriptItem &si = layoutData->items[item];
       
  1043     si.glyph_data_offset = layoutData->used;
       
  1044 
       
  1045     QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent);
       
  1046 
       
  1047     QTextEngine::ShaperFlags flags;
       
  1048     if (si.analysis.bidiLevel % 2)
       
  1049         flags |= RightToLeft;
       
  1050     if (option.useDesignMetrics())
       
  1051 	flags |= DesignMetrics;
       
  1052 
       
  1053     attributes(); // pre-initialize char attributes
       
  1054 
       
  1055     const int len = length(item);
       
  1056     int num_glyphs = length(item);
       
  1057     const QChar *str = layoutData->string.unicode() + si.position;
       
  1058     ushort upperCased[256];
       
  1059     if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
       
  1060             || si.analysis.flags == QScriptAnalysis::Lowercase) {
       
  1061         ushort *uc = upperCased;
       
  1062         if (len > 256)
       
  1063             uc = new ushort[len];
       
  1064         for (int i = 0; i < len; ++i) {
       
  1065             if(si.analysis.flags == QScriptAnalysis::Lowercase)
       
  1066                 uc[i] = str[i].toLower().unicode();
       
  1067             else
       
  1068                 uc[i] = str[i].toUpper().unicode();
       
  1069         }
       
  1070         str = reinterpret_cast<const QChar *>(uc);
       
  1071     }
       
  1072 
       
  1073     while (true) {
       
  1074         ensureSpace(num_glyphs);
       
  1075         num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
       
  1076 
       
  1077         QGlyphLayout g = availableGlyphs(&si);
       
  1078         unsigned short *log_clusters = logClusters(&si);
       
  1079 
       
  1080         if (fe->stringToCMap(str,
       
  1081                              len,
       
  1082                              &g,
       
  1083                              &num_glyphs,
       
  1084                              flags)) {
       
  1085             heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
       
  1086 		    break;
       
  1087         }
       
  1088     }
       
  1089 
       
  1090     si.num_glyphs = num_glyphs;
       
  1091 
       
  1092     layoutData->used += si.num_glyphs;
       
  1093 
       
  1094     const ushort *uc = reinterpret_cast<const ushort *>(str);
       
  1095     if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
       
  1096          || si.analysis.flags == QScriptAnalysis::Lowercase)
       
  1097         && uc != upperCased)
       
  1098         delete [] uc;
       
  1099 }
       
  1100 #endif
       
  1101 
       
  1102 static inline void moveGlyphData(const QGlyphLayout &destination, const QGlyphLayout &source, int num)
       
  1103 {
       
  1104     if (num > 0 && destination.glyphs != source.glyphs) {
       
  1105         memmove(destination.glyphs, source.glyphs, num * sizeof(HB_Glyph));
       
  1106         memmove(destination.attributes, source.attributes, num * sizeof(HB_GlyphAttributes));
       
  1107         memmove(destination.advances_x, source.advances_x, num * sizeof(HB_Fixed));
       
  1108         memmove(destination.offsets, source.offsets, num * sizeof(HB_FixedPoint));
       
  1109     }
       
  1110 }
       
  1111 
       
  1112 /// take the item from layoutData->items and
       
  1113 void QTextEngine::shapeTextWithHarfbuzz(int item) const
       
  1114 {
       
  1115     Q_ASSERT(sizeof(HB_Fixed) == sizeof(QFixed));
       
  1116     Q_ASSERT(sizeof(HB_FixedPoint) == sizeof(QFixedPoint));
       
  1117 
       
  1118     QScriptItem &si = layoutData->items[item];
       
  1119 
       
  1120     si.glyph_data_offset = layoutData->used;
       
  1121 
       
  1122     QFontEngine *font = fontEngine(si, &si.ascent, &si.descent);
       
  1123 
       
  1124     bool kerningEnabled = this->font(si).d->kerning;
       
  1125 
       
  1126     HB_ShaperItem entire_shaper_item;
       
  1127     entire_shaper_item.kerning_applied = false;
       
  1128     entire_shaper_item.string = reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData());
       
  1129     entire_shaper_item.stringLength = layoutData->string.length();
       
  1130     entire_shaper_item.item.script = (HB_Script)si.analysis.script;
       
  1131     entire_shaper_item.item.pos = si.position;
       
  1132     entire_shaper_item.item.length = length(item);
       
  1133     entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel;
       
  1134     entire_shaper_item.glyphIndicesPresent = false;
       
  1135 
       
  1136     HB_UChar16 upperCased[256]; // XXX what about making this 4096, so we don't have to extend it ever.
       
  1137     if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
       
  1138             || si.analysis.flags == QScriptAnalysis::Lowercase) {
       
  1139         HB_UChar16 *uc = upperCased;
       
  1140         if (entire_shaper_item.item.length > 256)
       
  1141             uc = new HB_UChar16[entire_shaper_item.item.length];
       
  1142         for (uint i = 0; i < entire_shaper_item.item.length; ++i) {
       
  1143             if(si.analysis.flags == QScriptAnalysis::Lowercase)
       
  1144                 uc[i] = QChar::toLower(entire_shaper_item.string[si.position + i]);
       
  1145             else
       
  1146                 uc[i] = QChar::toUpper(entire_shaper_item.string[si.position + i]);
       
  1147         }
       
  1148         entire_shaper_item.item.pos = 0;
       
  1149         entire_shaper_item.string = uc;
       
  1150         entire_shaper_item.stringLength = entire_shaper_item.item.length;
       
  1151     }
       
  1152 
       
  1153     entire_shaper_item.shaperFlags = 0;
       
  1154     if (!kerningEnabled)
       
  1155         entire_shaper_item.shaperFlags |= HB_ShaperFlag_NoKerning;
       
  1156     if (option.useDesignMetrics())
       
  1157         entire_shaper_item.shaperFlags |= HB_ShaperFlag_UseDesignMetrics;
       
  1158 
       
  1159     entire_shaper_item.num_glyphs = qMax(layoutData->glyphLayout.numGlyphs - layoutData->used, int(entire_shaper_item.item.length));
       
  1160     ensureSpace(entire_shaper_item.num_glyphs);
       
  1161     QGlyphLayout initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
       
  1162 
       
  1163     if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
       
  1164         ensureSpace(entire_shaper_item.num_glyphs);
       
  1165         initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
       
  1166 
       
  1167         if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
       
  1168             // ############ if this happens there's a bug in the fontengine
       
  1169             if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
       
  1170                     || si.analysis.flags == QScriptAnalysis::Lowercase) && entire_shaper_item.string != upperCased)
       
  1171                 delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
       
  1172             return;
       
  1173         }
       
  1174     }
       
  1175 
       
  1176     // split up the item into parts that come from different font engines.
       
  1177     QVarLengthArray<int> itemBoundaries(2);
       
  1178     // k * 2 entries, array[k] == index in string, array[k + 1] == index in glyphs
       
  1179     itemBoundaries[0] = entire_shaper_item.item.pos;
       
  1180     itemBoundaries[1] = 0;
       
  1181 
       
  1182     if (font->type() == QFontEngine::Multi) {
       
  1183         uint lastEngine = 0;
       
  1184         int charIdx = entire_shaper_item.item.pos;
       
  1185         const int stringEnd = charIdx + entire_shaper_item.item.length;
       
  1186         for (quint32 i = 0; i < entire_shaper_item.num_glyphs; ++i, ++charIdx) {
       
  1187             uint engineIdx = initialGlyphs.glyphs[i] >> 24;
       
  1188             if (engineIdx != lastEngine && i > 0) {
       
  1189                 itemBoundaries.append(charIdx);
       
  1190                 itemBoundaries.append(i);
       
  1191             }
       
  1192             lastEngine = engineIdx;
       
  1193             if (HB_IsHighSurrogate(entire_shaper_item.string[charIdx])
       
  1194                 && charIdx < stringEnd - 1
       
  1195                 && HB_IsLowSurrogate(entire_shaper_item.string[charIdx + 1]))
       
  1196                 ++charIdx;
       
  1197         }
       
  1198     }
       
  1199 
       
  1200 
       
  1201 
       
  1202     int remaining_glyphs = entire_shaper_item.num_glyphs;
       
  1203     int glyph_pos = 0;
       
  1204     // for each item shape using harfbuzz and store the results in our layoutData's glyphs array.
       
  1205     for (int k = 0; k < itemBoundaries.size(); k += 2) { // for the +2, see the comment at the definition of itemBoundaries
       
  1206 
       
  1207         HB_ShaperItem shaper_item = entire_shaper_item;
       
  1208 
       
  1209         shaper_item.item.pos = itemBoundaries[k];
       
  1210         if (k < itemBoundaries.size() - 3) {
       
  1211             shaper_item.item.length = itemBoundaries[k + 2] - shaper_item.item.pos;
       
  1212             shaper_item.num_glyphs = itemBoundaries[k + 3] - itemBoundaries[k + 1];
       
  1213         } else { // last combo in the list, avoid out of bounds access.
       
  1214             shaper_item.item.length -= shaper_item.item.pos - entire_shaper_item.item.pos;
       
  1215             shaper_item.num_glyphs -= itemBoundaries[k + 1];
       
  1216         }
       
  1217         shaper_item.initialGlyphCount = shaper_item.num_glyphs;
       
  1218 
       
  1219         QFontEngine *actualFontEngine = font;
       
  1220         uint engineIdx = 0;
       
  1221         if (font->type() == QFontEngine::Multi) {
       
  1222             engineIdx = uint(availableGlyphs(&si).glyphs[glyph_pos] >> 24);
       
  1223 
       
  1224             actualFontEngine = static_cast<QFontEngineMulti *>(font)->engine(engineIdx);
       
  1225         }
       
  1226 
       
  1227         shaper_item.font = actualFontEngine->harfbuzzFont();
       
  1228         shaper_item.face = actualFontEngine->harfbuzzFace();
       
  1229 
       
  1230         shaper_item.glyphIndicesPresent = true;
       
  1231 
       
  1232         remaining_glyphs -= shaper_item.initialGlyphCount;
       
  1233 
       
  1234         do {
       
  1235             ensureSpace(glyph_pos + shaper_item.num_glyphs + remaining_glyphs);
       
  1236 
       
  1237             const QGlyphLayout g = availableGlyphs(&si).mid(glyph_pos);
       
  1238             moveGlyphData(g.mid(shaper_item.num_glyphs), g.mid(shaper_item.initialGlyphCount), remaining_glyphs);
       
  1239 
       
  1240             shaper_item.glyphs = g.glyphs;
       
  1241             shaper_item.attributes = g.attributes;
       
  1242             shaper_item.advances = reinterpret_cast<HB_Fixed *>(g.advances_x);
       
  1243             shaper_item.offsets = reinterpret_cast<HB_FixedPoint *>(g.offsets);
       
  1244 
       
  1245             if (shaper_item.glyphIndicesPresent) {
       
  1246                 for (hb_uint32 i = 0; i < shaper_item.initialGlyphCount; ++i)
       
  1247                     shaper_item.glyphs[i] &= 0x00ffffff;
       
  1248             }
       
  1249 
       
  1250             shaper_item.log_clusters = logClusters(&si) + shaper_item.item.pos - entire_shaper_item.item.pos;
       
  1251 
       
  1252 //          qDebug("    .. num_glyphs=%d, used=%d, item.num_glyphs=%d", num_glyphs, used, shaper_item.num_glyphs);
       
  1253         } while (!qShapeItem(&shaper_item)); // this does the actual shaping via harfbuzz.
       
  1254 
       
  1255         QGlyphLayout g = availableGlyphs(&si).mid(glyph_pos, shaper_item.num_glyphs);
       
  1256         moveGlyphData(g.mid(shaper_item.num_glyphs), g.mid(shaper_item.initialGlyphCount), remaining_glyphs);
       
  1257 
       
  1258         for (hb_uint32 i = 0; i < shaper_item.num_glyphs; ++i)
       
  1259             g.glyphs[i] = g.glyphs[i] | (engineIdx << 24);
       
  1260 
       
  1261         for (hb_uint32 i = 0; i < shaper_item.item.length; ++i)
       
  1262             shaper_item.log_clusters[i] += glyph_pos;
       
  1263 
       
  1264         if (kerningEnabled && !shaper_item.kerning_applied)
       
  1265             font->doKerning(&g, option.useDesignMetrics() ? QFlag(QTextEngine::DesignMetrics) : QFlag(0));
       
  1266 
       
  1267         glyph_pos += shaper_item.num_glyphs;
       
  1268     }
       
  1269 
       
  1270 //     qDebug("    -> item: script=%d num_glyphs=%d", shaper_item.script, shaper_item.num_glyphs);
       
  1271     si.num_glyphs = glyph_pos;
       
  1272 
       
  1273     layoutData->used += si.num_glyphs;
       
  1274 
       
  1275     if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase)
       
  1276         && entire_shaper_item.string != upperCased)
       
  1277         delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
       
  1278 }
       
  1279 
       
  1280 static void init(QTextEngine *e)
       
  1281 {
       
  1282     e->ignoreBidi = false;
       
  1283     e->cacheGlyphs = false;
       
  1284     e->forceJustification = false;
       
  1285 
       
  1286     e->layoutData = 0;
       
  1287 
       
  1288     e->minWidth = 0;
       
  1289     e->maxWidth = 0;
       
  1290 
       
  1291     e->underlinePositions = 0;
       
  1292     e->specialData = 0;
       
  1293     e->stackEngine = false;
       
  1294 }
       
  1295 
       
  1296 QTextEngine::QTextEngine()
       
  1297 {
       
  1298     init(this);
       
  1299 }
       
  1300 
       
  1301 QTextEngine::QTextEngine(const QString &str, const QFont &f)
       
  1302     : fnt(f)
       
  1303 {
       
  1304     init(this);
       
  1305     text = str;
       
  1306 }
       
  1307 
       
  1308 QTextEngine::~QTextEngine()
       
  1309 {
       
  1310     if (!stackEngine)
       
  1311         delete layoutData;
       
  1312     delete specialData;
       
  1313 }
       
  1314 
       
  1315 const HB_CharAttributes *QTextEngine::attributes() const
       
  1316 {
       
  1317     if (layoutData && layoutData->haveCharAttributes)
       
  1318         return (HB_CharAttributes *) layoutData->memory;
       
  1319 
       
  1320     itemize();
       
  1321     ensureSpace(layoutData->string.length());
       
  1322 
       
  1323     QVarLengthArray<HB_ScriptItem> hbScriptItems(layoutData->items.size());
       
  1324 
       
  1325     for (int i = 0; i < layoutData->items.size(); ++i) {
       
  1326         const QScriptItem &si = layoutData->items[i];
       
  1327         hbScriptItems[i].pos = si.position;
       
  1328         hbScriptItems[i].length = length(i);
       
  1329         hbScriptItems[i].bidiLevel = si.analysis.bidiLevel;
       
  1330         hbScriptItems[i].script = (HB_Script)si.analysis.script;
       
  1331     }
       
  1332 
       
  1333     qGetCharAttributes(reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData()),
       
  1334                        layoutData->string.length(),
       
  1335                        hbScriptItems.data(), hbScriptItems.size(),
       
  1336                        (HB_CharAttributes *)layoutData->memory);
       
  1337 
       
  1338 
       
  1339     layoutData->haveCharAttributes = true;
       
  1340     return (HB_CharAttributes *) layoutData->memory;
       
  1341 }
       
  1342 
       
  1343 void QTextEngine::shape(int item) const
       
  1344 {
       
  1345     if (layoutData->items[item].analysis.flags == QScriptAnalysis::Object) {
       
  1346         ensureSpace(1);
       
  1347         if (block.docHandle()) {
       
  1348             QTextFormat format = formats()->format(formatIndex(&layoutData->items[item]));
       
  1349             docLayout()->resizeInlineObject(QTextInlineObject(item, const_cast<QTextEngine *>(this)),
       
  1350                                             layoutData->items[item].position + block.position(), format);
       
  1351         }
       
  1352     } else if (layoutData->items[item].analysis.flags == QScriptAnalysis::Tab) {
       
  1353         // set up at least the ascent/descent of the script item for the tab
       
  1354         fontEngine(layoutData->items[item], &layoutData->items[item].ascent, &layoutData->items[item].descent);
       
  1355     } else {
       
  1356         shapeText(item);
       
  1357     }
       
  1358 }
       
  1359 
       
  1360 void QTextEngine::invalidate()
       
  1361 {
       
  1362     freeMemory();
       
  1363     minWidth = 0;
       
  1364     maxWidth = 0;
       
  1365     if (specialData)
       
  1366         specialData->resolvedFormatIndices.clear();
       
  1367 }
       
  1368 
       
  1369 void QTextEngine::clearLineData()
       
  1370 {
       
  1371     lines.clear();
       
  1372 }
       
  1373 
       
  1374 void QTextEngine::validate() const
       
  1375 {
       
  1376     if (layoutData)
       
  1377         return;
       
  1378     layoutData = new LayoutData();
       
  1379     if (block.docHandle()) {
       
  1380         layoutData->string = block.text();
       
  1381         if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
       
  1382             layoutData->string += QLatin1Char(block.next().isValid() ? 0xb6 : 0x20);
       
  1383     } else {
       
  1384         layoutData->string = text;
       
  1385     }
       
  1386     if (specialData && specialData->preeditPosition != -1)
       
  1387         layoutData->string.insert(specialData->preeditPosition, specialData->preeditText);
       
  1388 }
       
  1389 
       
  1390 void QTextEngine::itemize() const
       
  1391 {
       
  1392     validate();
       
  1393     if (layoutData->items.size())
       
  1394         return;
       
  1395 
       
  1396     int length = layoutData->string.length();
       
  1397     if (!length)
       
  1398         return;
       
  1399 #if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
       
  1400     // ATSUI requires RTL flags to correctly identify the character stops.
       
  1401     bool ignore = false;
       
  1402 #else
       
  1403     bool ignore = ignoreBidi;
       
  1404 #endif
       
  1405     if (!ignore && option.textDirection() == Qt::LeftToRight) {
       
  1406         ignore = true;
       
  1407         const QChar *start = layoutData->string.unicode();
       
  1408         const QChar * const end = start + length;
       
  1409         while (start < end) {
       
  1410             if (start->unicode() >= 0x590) {
       
  1411                 ignore = false;
       
  1412                 break;
       
  1413             }
       
  1414             ++start;
       
  1415         }
       
  1416     }
       
  1417 
       
  1418     QVarLengthArray<QScriptAnalysis, 4096> scriptAnalysis(length);
       
  1419     QScriptAnalysis *analysis = scriptAnalysis.data();
       
  1420 
       
  1421     QBidiControl control(option.textDirection() == Qt::RightToLeft);
       
  1422 
       
  1423     if (ignore) {
       
  1424         memset(analysis, 0, length*sizeof(QScriptAnalysis));
       
  1425         if (option.textDirection() == Qt::RightToLeft) {
       
  1426             for (int i = 0; i < length; ++i)
       
  1427                 analysis[i].bidiLevel = 1;
       
  1428             layoutData->hasBidi = true;
       
  1429         }
       
  1430     } else {
       
  1431         layoutData->hasBidi = bidiItemize(const_cast<QTextEngine *>(this), analysis, control);
       
  1432     }
       
  1433 
       
  1434     const ushort *unicode = layoutData->string.utf16();
       
  1435     // correctly assign script, isTab and isObject to the script analysis
       
  1436     const ushort *uc = unicode;
       
  1437     const ushort *e = uc + length;
       
  1438     int lastScript = QUnicodeTables::Common;
       
  1439     while (uc < e) {
       
  1440         int script = QUnicodeTables::script(*uc);
       
  1441         if (script == QUnicodeTables::Inherited)
       
  1442             script = lastScript;
       
  1443         analysis->flags = QScriptAnalysis::None;
       
  1444         if (*uc == QChar::ObjectReplacementCharacter) {
       
  1445             if (analysis->bidiLevel % 2)
       
  1446                 --analysis->bidiLevel;
       
  1447             analysis->script = QUnicodeTables::Common;
       
  1448             analysis->flags = QScriptAnalysis::Object;
       
  1449         } else if (*uc == QChar::LineSeparator) {
       
  1450             if (analysis->bidiLevel % 2)
       
  1451                 --analysis->bidiLevel;
       
  1452             analysis->script = QUnicodeTables::Common;
       
  1453             analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
       
  1454             if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
       
  1455                 *const_cast<ushort*>(uc) = 0x21B5; // visual line separator
       
  1456         } else if (*uc == 9) {
       
  1457             analysis->script = QUnicodeTables::Common;
       
  1458             analysis->flags = QScriptAnalysis::Tab;
       
  1459             analysis->bidiLevel = control.baseLevel();
       
  1460         } else if ((*uc == 32 || *uc == QChar::Nbsp)
       
  1461                    && (option.flags() & QTextOption::ShowTabsAndSpaces)) {
       
  1462             analysis->script = QUnicodeTables::Common;
       
  1463             analysis->flags = QScriptAnalysis::Space;
       
  1464             analysis->bidiLevel = control.baseLevel();
       
  1465         } else {
       
  1466             analysis->script = script;
       
  1467         }
       
  1468         lastScript = analysis->script;
       
  1469         ++uc;
       
  1470         ++analysis;
       
  1471     }
       
  1472     if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
       
  1473         (analysis-1)->flags = QScriptAnalysis::LineOrParagraphSeparator; // to exclude it from width
       
  1474     }
       
  1475 
       
  1476     Itemizer itemizer(layoutData->string, scriptAnalysis.data(), layoutData->items);
       
  1477 
       
  1478     const QTextDocumentPrivate *p = block.docHandle();
       
  1479     if (p) {
       
  1480         SpecialData *s = specialData;
       
  1481 
       
  1482         QTextDocumentPrivate::FragmentIterator it = p->find(block.position());
       
  1483         QTextDocumentPrivate::FragmentIterator end = p->find(block.position() + block.length() - 1); // -1 to omit the block separator char
       
  1484         int format = it.value()->format;
       
  1485 
       
  1486         int prevPosition = 0;
       
  1487         int position = prevPosition;
       
  1488         while (1) {
       
  1489             const QTextFragmentData * const frag = it.value();
       
  1490             if (it == end || format != frag->format) {
       
  1491                 if (s && position >= s->preeditPosition) {
       
  1492                     position += s->preeditText.length();
       
  1493                     s = 0;
       
  1494                 }
       
  1495                 Q_ASSERT(position <= length);
       
  1496                 itemizer.generate(prevPosition, position - prevPosition,
       
  1497                     formats()->charFormat(format).fontCapitalization());
       
  1498                 if (it == end) {
       
  1499                     if (position < length)
       
  1500                         itemizer.generate(position, length - position,
       
  1501                                           formats()->charFormat(format).fontCapitalization());
       
  1502                     break;
       
  1503                 }
       
  1504                 format = frag->format;
       
  1505                 prevPosition = position;
       
  1506             }
       
  1507             position += frag->size_array[0];
       
  1508             ++it;
       
  1509         }
       
  1510     } else {
       
  1511         itemizer.generate(0, length, static_cast<QFont::Capitalization> (fnt.d->capital));
       
  1512     }
       
  1513 
       
  1514     addRequiredBoundaries();
       
  1515     resolveAdditionalFormats();
       
  1516 }
       
  1517 
       
  1518 int QTextEngine::findItem(int strPos) const
       
  1519 {
       
  1520     itemize();
       
  1521 
       
  1522     // ##### use binary search
       
  1523     int item;
       
  1524     for (item = layoutData->items.size()-1; item > 0; --item) {
       
  1525         if (layoutData->items[item].position <= strPos)
       
  1526             break;
       
  1527     }
       
  1528     return item;
       
  1529 }
       
  1530 
       
  1531 QFixed QTextEngine::width(int from, int len) const
       
  1532 {
       
  1533     itemize();
       
  1534 
       
  1535     QFixed w = 0;
       
  1536 
       
  1537 //     qDebug("QTextEngine::width(from = %d, len = %d), numItems=%d, strleng=%d", from,  len, items.size(), string.length());
       
  1538     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1539         const QScriptItem *si = layoutData->items.constData() + i;
       
  1540         int pos = si->position;
       
  1541         int ilen = length(i);
       
  1542 //          qDebug("item %d: from %d len %d", i, pos, ilen);
       
  1543         if (pos >= from + len)
       
  1544             break;
       
  1545         if (pos + ilen > from) {
       
  1546             if (!si->num_glyphs)
       
  1547                 shape(i);
       
  1548 
       
  1549             if (si->analysis.flags == QScriptAnalysis::Object) {
       
  1550                 w += si->width;
       
  1551                 continue;
       
  1552             } else if (si->analysis.flags == QScriptAnalysis::Tab) {
       
  1553                 w += calculateTabWidth(i, w);
       
  1554                 continue;
       
  1555             }
       
  1556 
       
  1557 
       
  1558             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1559             unsigned short *logClusters = this->logClusters(si);
       
  1560 
       
  1561 //             fprintf(stderr, "  logclusters:");
       
  1562 //             for (int k = 0; k < ilen; k++)
       
  1563 //                 fprintf(stderr, " %d", logClusters[k]);
       
  1564 //             fprintf(stderr, "\n");
       
  1565             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1566             int charFrom = from - pos;
       
  1567             if (charFrom < 0)
       
  1568                 charFrom = 0;
       
  1569             int glyphStart = logClusters[charFrom];
       
  1570             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1571                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1572                     charFrom++;
       
  1573             if (charFrom < ilen) {
       
  1574                 glyphStart = logClusters[charFrom];
       
  1575                 int charEnd = from + len - 1 - pos;
       
  1576                 if (charEnd >= ilen)
       
  1577                     charEnd = ilen-1;
       
  1578                 int glyphEnd = logClusters[charEnd];
       
  1579                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1580                     charEnd++;
       
  1581                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1582 
       
  1583 //                 qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd);
       
  1584                 for (int i = glyphStart; i < glyphEnd; i++)
       
  1585                     w += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
       
  1586             }
       
  1587         }
       
  1588     }
       
  1589 //     qDebug("   --> w= %d ", w);
       
  1590     return w;
       
  1591 }
       
  1592 
       
  1593 glyph_metrics_t QTextEngine::boundingBox(int from,  int len) const
       
  1594 {
       
  1595     itemize();
       
  1596 
       
  1597     glyph_metrics_t gm;
       
  1598 
       
  1599     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1600         const QScriptItem *si = layoutData->items.constData() + i;
       
  1601         QFontEngine *fe = fontEngine(*si);
       
  1602 
       
  1603         int pos = si->position;
       
  1604         int ilen = length(i);
       
  1605         if (pos > from + len)
       
  1606             break;
       
  1607         if (pos + ilen > from) {
       
  1608             if (!si->num_glyphs)
       
  1609                 shape(i);
       
  1610 
       
  1611             if (si->analysis.flags == QScriptAnalysis::Object) {
       
  1612                 gm.width += si->width;
       
  1613                 continue;
       
  1614             } else if (si->analysis.flags == QScriptAnalysis::Tab) {
       
  1615                 gm.width += calculateTabWidth(i, gm.width);
       
  1616                 continue;
       
  1617             }
       
  1618 
       
  1619             unsigned short *logClusters = this->logClusters(si);
       
  1620             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1621 
       
  1622             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1623             int charFrom = from - pos;
       
  1624             if (charFrom < 0)
       
  1625                 charFrom = 0;
       
  1626             int glyphStart = logClusters[charFrom];
       
  1627             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1628                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1629                     charFrom++;
       
  1630             if (charFrom < ilen) {
       
  1631                 glyphStart = logClusters[charFrom];
       
  1632                 int charEnd = from + len - 1 - pos;
       
  1633                 if (charEnd >= ilen)
       
  1634                     charEnd = ilen-1;
       
  1635                 int glyphEnd = logClusters[charEnd];
       
  1636                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1637                     charEnd++;
       
  1638                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1639                 if (glyphStart <= glyphEnd ) {
       
  1640                     glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
       
  1641                     gm.x = qMin(gm.x, m.x + gm.xoff);
       
  1642                     gm.y = qMin(gm.y, m.y + gm.yoff);
       
  1643                     gm.width = qMax(gm.width, m.width+gm.xoff);
       
  1644                     gm.height = qMax(gm.height, m.height+gm.yoff);
       
  1645                     gm.xoff += m.xoff;
       
  1646                     gm.yoff += m.yoff;
       
  1647                 }
       
  1648             }
       
  1649 
       
  1650             glyph_t glyph = glyphs.glyphs[logClusters[pos + ilen - 1]];
       
  1651             glyph_metrics_t gi = fe->boundingBox(glyph);
       
  1652             if (gi.isValid())
       
  1653                 gm.width -= qRound(gi.xoff - gi.x - gi.width);
       
  1654         }
       
  1655     }
       
  1656     return gm;
       
  1657 }
       
  1658 
       
  1659 glyph_metrics_t QTextEngine::tightBoundingBox(int from,  int len) const
       
  1660 {
       
  1661     itemize();
       
  1662 
       
  1663     glyph_metrics_t gm;
       
  1664 
       
  1665     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1666         const QScriptItem *si = layoutData->items.constData() + i;
       
  1667         int pos = si->position;
       
  1668         int ilen = length(i);
       
  1669         if (pos > from + len)
       
  1670             break;
       
  1671         if (pos + len > from) {
       
  1672             if (!si->num_glyphs)
       
  1673                 shape(i);
       
  1674             unsigned short *logClusters = this->logClusters(si);
       
  1675             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1676 
       
  1677             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1678             int charFrom = from - pos;
       
  1679             if (charFrom < 0)
       
  1680                 charFrom = 0;
       
  1681             int glyphStart = logClusters[charFrom];
       
  1682             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1683                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1684                     charFrom++;
       
  1685             if (charFrom < ilen) {
       
  1686                 glyphStart = logClusters[charFrom];
       
  1687                 int charEnd = from + len - 1 - pos;
       
  1688                 if (charEnd >= ilen)
       
  1689                     charEnd = ilen-1;
       
  1690                 int glyphEnd = logClusters[charEnd];
       
  1691                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1692                     charEnd++;
       
  1693                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1694                 if (glyphStart <= glyphEnd ) {
       
  1695                     QFontEngine *fe = fontEngine(*si);
       
  1696                     glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
       
  1697                     gm.x = qMin(gm.x, m.x + gm.xoff);
       
  1698                     gm.y = qMin(gm.y, m.y + gm.yoff);
       
  1699                     gm.width = qMax(gm.width, m.width+gm.xoff);
       
  1700                     gm.height = qMax(gm.height, m.height+gm.yoff);
       
  1701                     gm.xoff += m.xoff;
       
  1702                     gm.yoff += m.yoff;
       
  1703                 }
       
  1704             }
       
  1705         }
       
  1706     }
       
  1707     return gm;
       
  1708 }
       
  1709 
       
  1710 QFont QTextEngine::font(const QScriptItem &si) const
       
  1711 {
       
  1712     QFont font = fnt;
       
  1713     if (hasFormats()) {
       
  1714         QTextCharFormat f = format(&si);
       
  1715         font = f.font();
       
  1716 
       
  1717         if (block.docHandle() && block.docHandle()->layout()) {
       
  1718             // Make sure we get the right dpi on printers
       
  1719             QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  1720             if (pdev)
       
  1721                 font = QFont(font, pdev);
       
  1722         } else {
       
  1723             font = font.resolve(fnt);
       
  1724         }
       
  1725         QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
       
  1726         if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  1727             if (font.pointSize() != -1)
       
  1728                 font.setPointSize((font.pointSize() * 2) / 3);
       
  1729             else
       
  1730                 font.setPixelSize((font.pixelSize() * 2) / 3);
       
  1731         }
       
  1732     }
       
  1733 
       
  1734     if (si.analysis.flags == QScriptAnalysis::SmallCaps)
       
  1735         font = font.d->smallCapsFont();
       
  1736 
       
  1737     return font;
       
  1738 }
       
  1739 
       
  1740 QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent) const
       
  1741 {
       
  1742     QFontEngine *engine = 0;
       
  1743     QFontEngine *scaledEngine = 0;
       
  1744     int script = si.analysis.script;
       
  1745 
       
  1746     QFont font = fnt;
       
  1747     if (hasFormats()) {
       
  1748         QTextCharFormat f = format(&si);
       
  1749         font = f.font();
       
  1750 
       
  1751         if (block.docHandle() && block.docHandle()->layout()) {
       
  1752             // Make sure we get the right dpi on printers
       
  1753             QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  1754             if (pdev)
       
  1755                 font = QFont(font, pdev);
       
  1756         } else {
       
  1757             font = font.resolve(fnt);
       
  1758         }
       
  1759         engine = font.d->engineForScript(script);
       
  1760         QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
       
  1761         if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  1762             if (font.pointSize() != -1)
       
  1763                 font.setPointSize((font.pointSize() * 2) / 3);
       
  1764             else
       
  1765                 font.setPixelSize((font.pixelSize() * 2) / 3);
       
  1766             scaledEngine = font.d->engineForScript(script);
       
  1767         }
       
  1768     } else {
       
  1769         engine = font.d->engineForScript(script);
       
  1770     }
       
  1771 
       
  1772     if (si.analysis.flags == QScriptAnalysis::SmallCaps) {
       
  1773         QFontPrivate *p = font.d->smallCapsFontPrivate();
       
  1774         scaledEngine = p->engineForScript(script);
       
  1775     }
       
  1776 
       
  1777     if (ascent) {
       
  1778         *ascent = engine->ascent();
       
  1779         *descent = engine->descent();
       
  1780     }
       
  1781 
       
  1782     if (scaledEngine)
       
  1783         return scaledEngine;
       
  1784     return engine;
       
  1785 }
       
  1786 
       
  1787 struct QJustificationPoint {
       
  1788     int type;
       
  1789     QFixed kashidaWidth;
       
  1790     QGlyphLayout glyph;
       
  1791     QFontEngine *fontEngine;
       
  1792 };
       
  1793 
       
  1794 Q_DECLARE_TYPEINFO(QJustificationPoint, Q_PRIMITIVE_TYPE);
       
  1795 
       
  1796 static void set(QJustificationPoint *point, int type, const QGlyphLayout &glyph, QFontEngine *fe)
       
  1797 {
       
  1798     point->type = type;
       
  1799     point->glyph = glyph;
       
  1800     point->fontEngine = fe;
       
  1801 
       
  1802     if (type >= HB_Arabic_Normal) {
       
  1803         QChar ch(0x640); // Kashida character
       
  1804         QGlyphLayoutArray<8> glyphs;
       
  1805         int nglyphs = 7;
       
  1806         fe->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
       
  1807         if (glyphs.glyphs[0] && glyphs.advances_x[0] != 0) {
       
  1808             point->kashidaWidth = glyphs.advances_x[0];
       
  1809         } else {
       
  1810             point->type = HB_NoJustification;
       
  1811             point->kashidaWidth = 0;
       
  1812         }
       
  1813     }
       
  1814 }
       
  1815 
       
  1816 
       
  1817 void QTextEngine::justify(const QScriptLine &line)
       
  1818 {
       
  1819 //     qDebug("justify: line.gridfitted = %d, line.justified=%d", line.gridfitted, line.justified);
       
  1820     if (line.gridfitted && line.justified)
       
  1821         return;
       
  1822 
       
  1823     if (!line.gridfitted) {
       
  1824         // redo layout in device metrics, then adjust
       
  1825         const_cast<QScriptLine &>(line).gridfitted = true;
       
  1826     }
       
  1827 
       
  1828     if ((option.alignment() & Qt::AlignHorizontal_Mask) != Qt::AlignJustify)
       
  1829         return;
       
  1830 
       
  1831     itemize();
       
  1832 
       
  1833     if (!forceJustification) {
       
  1834         int end = line.from + (int)line.length;
       
  1835         if (end == layoutData->string.length())
       
  1836             return; // no justification at end of paragraph
       
  1837         if (end && layoutData->items[findItem(end-1)].analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
       
  1838             return; // no justification at the end of an explicitely separated line
       
  1839     }
       
  1840 
       
  1841     // justify line
       
  1842     int maxJustify = 0;
       
  1843 
       
  1844     // don't include trailing white spaces when doing justification
       
  1845     int line_length = line.length;
       
  1846     const HB_CharAttributes *a = attributes()+line.from;
       
  1847     while (line_length && a[line_length-1].whiteSpace)
       
  1848         --line_length;
       
  1849     // subtract one char more, as we can't justfy after the last character
       
  1850     --line_length;
       
  1851 
       
  1852     if (!line_length)
       
  1853         return;
       
  1854 
       
  1855     int firstItem = findItem(line.from);
       
  1856     int nItems = findItem(line.from + line_length - 1) - firstItem + 1;
       
  1857 
       
  1858     QVarLengthArray<QJustificationPoint> justificationPoints;
       
  1859     int nPoints = 0;
       
  1860 //     qDebug("justifying from %d len %d, firstItem=%d, nItems=%d (%s)", line.from, line_length, firstItem, nItems, layoutData->string.mid(line.from, line_length).toUtf8().constData());
       
  1861     QFixed minKashida = 0x100000;
       
  1862 
       
  1863     // we need to do all shaping before we go into the next loop, as we there
       
  1864     // store pointers to the glyph data that could get reallocated by the shaping
       
  1865     // process.
       
  1866     for (int i = 0; i < nItems; ++i) {
       
  1867         QScriptItem &si = layoutData->items[firstItem + i];
       
  1868         if (!si.num_glyphs)
       
  1869             shape(firstItem + i);
       
  1870     }
       
  1871 
       
  1872     for (int i = 0; i < nItems; ++i) {
       
  1873         QScriptItem &si = layoutData->items[firstItem + i];
       
  1874 
       
  1875         int kashida_type = HB_Arabic_Normal;
       
  1876         int kashida_pos = -1;
       
  1877 
       
  1878         int start = qMax(line.from - si.position, 0);
       
  1879         int end = qMin(line.from + line_length - (int)si.position, length(firstItem+i));
       
  1880 
       
  1881         unsigned short *log_clusters = logClusters(&si);
       
  1882 
       
  1883         int gs = log_clusters[start];
       
  1884         int ge = (end == length(firstItem+i) ? si.num_glyphs : log_clusters[end]);
       
  1885 
       
  1886         const QGlyphLayout g = shapedGlyphs(&si);
       
  1887 
       
  1888         for (int i = gs; i < ge; ++i) {
       
  1889             g.justifications[i].type = QGlyphJustification::JustifyNone;
       
  1890             g.justifications[i].nKashidas = 0;
       
  1891             g.justifications[i].space_18d6 = 0;
       
  1892 
       
  1893             justificationPoints.resize(nPoints+3);
       
  1894             int justification = g.attributes[i].justification;
       
  1895 
       
  1896             switch(justification) {
       
  1897             case HB_NoJustification:
       
  1898                 break;
       
  1899             case HB_Space          :
       
  1900                 // fall through
       
  1901             case HB_Arabic_Space   :
       
  1902                 if (kashida_pos >= 0) {
       
  1903 //                     qDebug("kashida position at %d in word", kashida_pos);
       
  1904                     set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
       
  1905                     minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
       
  1906                     maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
       
  1907                     ++nPoints;
       
  1908                 }
       
  1909                 kashida_pos = -1;
       
  1910                 kashida_type = HB_Arabic_Normal;
       
  1911                 // fall through
       
  1912             case HB_Character      :
       
  1913                 set(&justificationPoints[nPoints++], justification, g.mid(i), fontEngine(si));
       
  1914                 maxJustify = qMax(maxJustify, justification);
       
  1915                 break;
       
  1916             case HB_Arabic_Normal  :
       
  1917             case HB_Arabic_Waw     :
       
  1918             case HB_Arabic_BaRa    :
       
  1919             case HB_Arabic_Alef    :
       
  1920             case HB_Arabic_HaaDal  :
       
  1921             case HB_Arabic_Seen    :
       
  1922             case HB_Arabic_Kashida :
       
  1923                 if (justification >= kashida_type) {
       
  1924                     kashida_pos = i;
       
  1925                     kashida_type = justification;
       
  1926                 }
       
  1927             }
       
  1928         }
       
  1929         if (kashida_pos >= 0) {
       
  1930             set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
       
  1931             minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
       
  1932             maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
       
  1933             ++nPoints;
       
  1934         }
       
  1935     }
       
  1936 
       
  1937     QFixed need = line.width - line.textWidth;
       
  1938     if (need < 0) {
       
  1939         // line overflows already!
       
  1940         const_cast<QScriptLine &>(line).justified = true;
       
  1941         return;
       
  1942     }
       
  1943 
       
  1944 //     qDebug("doing justification: textWidth=%x, requested=%x, maxJustify=%d", line.textWidth.value(), line.width.value(), maxJustify);
       
  1945 //     qDebug("     minKashida=%f, need=%f", minKashida.toReal(), need.toReal());
       
  1946 
       
  1947     // distribute in priority order
       
  1948     if (maxJustify >= HB_Arabic_Normal) {
       
  1949         while (need >= minKashida) {
       
  1950             for (int type = maxJustify; need >= minKashida && type >= HB_Arabic_Normal; --type) {
       
  1951                 for (int i = 0; need >= minKashida && i < nPoints; ++i) {
       
  1952                     if (justificationPoints[i].type == type && justificationPoints[i].kashidaWidth <= need) {
       
  1953                         justificationPoints[i].glyph.justifications->nKashidas++;
       
  1954                         // ############
       
  1955                         justificationPoints[i].glyph.justifications->space_18d6 += justificationPoints[i].kashidaWidth.value();
       
  1956                         need -= justificationPoints[i].kashidaWidth;
       
  1957 //                         qDebug("adding kashida type %d with width %x, neednow %x", type, justificationPoints[i].kashidaWidth, need.value());
       
  1958                     }
       
  1959                 }
       
  1960             }
       
  1961         }
       
  1962     }
       
  1963     Q_ASSERT(need >= 0);
       
  1964     if (!need)
       
  1965         goto end;
       
  1966 
       
  1967     maxJustify = qMin(maxJustify, (int)HB_Space);
       
  1968     for (int type = maxJustify; need != 0 && type > 0; --type) {
       
  1969         int n = 0;
       
  1970         for (int i = 0; i < nPoints; ++i) {
       
  1971             if (justificationPoints[i].type == type)
       
  1972                 ++n;
       
  1973         }
       
  1974 //          qDebug("number of points for justification type %d: %d", type, n);
       
  1975 
       
  1976 
       
  1977         if (!n)
       
  1978             continue;
       
  1979 
       
  1980         for (int i = 0; i < nPoints; ++i) {
       
  1981             if (justificationPoints[i].type == type) {
       
  1982                 QFixed add = need/n;
       
  1983 //                  qDebug("adding %x to glyph %x", add.value(), justificationPoints[i].glyph->glyph);
       
  1984                 justificationPoints[i].glyph.justifications[0].space_18d6 = add.value();
       
  1985                 need -= add;
       
  1986                 --n;
       
  1987             }
       
  1988         }
       
  1989 
       
  1990         Q_ASSERT(!need);
       
  1991     }
       
  1992  end:
       
  1993     const_cast<QScriptLine &>(line).justified = true;
       
  1994 }
       
  1995 
       
  1996 void QScriptLine::setDefaultHeight(QTextEngine *eng)
       
  1997 {
       
  1998     QFont f;
       
  1999     QFontEngine *e;
       
  2000 
       
  2001     if (eng->block.docHandle() && eng->block.docHandle()->layout()) {
       
  2002         f = eng->block.charFormat().font();
       
  2003         // Make sure we get the right dpi on printers
       
  2004         QPaintDevice *pdev = eng->block.docHandle()->layout()->paintDevice();
       
  2005         if (pdev)
       
  2006             f = QFont(f, pdev);
       
  2007         e = f.d->engineForScript(QUnicodeTables::Common);
       
  2008     } else {
       
  2009         e = eng->fnt.d->engineForScript(QUnicodeTables::Common);
       
  2010     }
       
  2011 
       
  2012     ascent = qMax(ascent, e->ascent());
       
  2013     descent = qMax(descent, e->descent());
       
  2014 }
       
  2015 
       
  2016 QTextEngine::LayoutData::LayoutData()
       
  2017 {
       
  2018     memory = 0;
       
  2019     allocated = 0;
       
  2020     memory_on_stack = false;
       
  2021     used = 0;
       
  2022     hasBidi = false;
       
  2023     inLayout = false;
       
  2024     haveCharAttributes = false;
       
  2025     logClustersPtr = 0;
       
  2026     available_glyphs = 0;
       
  2027 }
       
  2028 
       
  2029 QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
       
  2030     : string(str)
       
  2031 {
       
  2032     allocated = _allocated;
       
  2033 
       
  2034     int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
       
  2035     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
       
  2036     available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::spaceNeededForGlyphLayout(1);
       
  2037 
       
  2038     if (available_glyphs < str.length()) {
       
  2039         // need to allocate on the heap
       
  2040         allocated = 0;
       
  2041 
       
  2042         memory_on_stack = false;
       
  2043         memory = 0;
       
  2044         logClustersPtr = 0;
       
  2045     } else {
       
  2046         memory_on_stack = true;
       
  2047         memory = stack_memory;
       
  2048         logClustersPtr = (unsigned short *)(memory + space_charAttributes);
       
  2049 
       
  2050         void *m = memory + space_charAttributes + space_logClusters;
       
  2051         glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
       
  2052         glyphLayout.clear();
       
  2053         memset(memory, 0, space_charAttributes*sizeof(void *));
       
  2054     }
       
  2055     used = 0;
       
  2056     hasBidi = false;
       
  2057     inLayout = false;
       
  2058     haveCharAttributes = false;
       
  2059 }
       
  2060 
       
  2061 QTextEngine::LayoutData::~LayoutData()
       
  2062 {
       
  2063     if (!memory_on_stack)
       
  2064         free(memory);
       
  2065     memory = 0;
       
  2066 }
       
  2067 
       
  2068 void QTextEngine::LayoutData::reallocate(int totalGlyphs)
       
  2069 {
       
  2070     Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs);
       
  2071     if (memory_on_stack && available_glyphs >= totalGlyphs) {
       
  2072         glyphLayout.grow(glyphLayout.data(), totalGlyphs);
       
  2073         return;
       
  2074     }
       
  2075 
       
  2076     int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
       
  2077     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
       
  2078     int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2;
       
  2079 
       
  2080     int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
       
  2081     Q_ASSERT(newAllocated >= allocated);
       
  2082     void **newMem = memory;
       
  2083     newMem = (void **)::realloc(memory_on_stack ? 0 : memory, newAllocated*sizeof(void *));
       
  2084     Q_CHECK_PTR(newMem);
       
  2085     if (memory_on_stack && newMem)
       
  2086         memcpy(newMem, memory, allocated*sizeof(void *));
       
  2087     memory = newMem;
       
  2088     memory_on_stack = false;
       
  2089 
       
  2090     void **m = memory;
       
  2091     m += space_charAttributes;
       
  2092     logClustersPtr = (unsigned short *) m;
       
  2093     m += space_logClusters;
       
  2094 
       
  2095     const int space_preGlyphLayout = space_charAttributes + space_logClusters;
       
  2096     if (allocated < space_preGlyphLayout)
       
  2097         memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
       
  2098 
       
  2099     glyphLayout.grow(reinterpret_cast<char *>(m), totalGlyphs);
       
  2100 
       
  2101     allocated = newAllocated;
       
  2102 }
       
  2103 
       
  2104 // grow to the new size, copying the existing data to the new layout
       
  2105 void QGlyphLayout::grow(char *address, int totalGlyphs)
       
  2106 {
       
  2107     QGlyphLayout oldLayout(address, numGlyphs);
       
  2108     QGlyphLayout newLayout(address, totalGlyphs);
       
  2109 
       
  2110     if (numGlyphs) {
       
  2111         // move the existing data
       
  2112         memmove(newLayout.attributes, oldLayout.attributes, numGlyphs * sizeof(HB_GlyphAttributes));
       
  2113         memmove(newLayout.justifications, oldLayout.justifications, numGlyphs * sizeof(QGlyphJustification));
       
  2114         memmove(newLayout.advances_y, oldLayout.advances_y, numGlyphs * sizeof(QFixed));
       
  2115         memmove(newLayout.advances_x, oldLayout.advances_x, numGlyphs * sizeof(QFixed));
       
  2116         memmove(newLayout.glyphs, oldLayout.glyphs, numGlyphs * sizeof(HB_Glyph));
       
  2117     }
       
  2118 
       
  2119     // clear the new data
       
  2120     newLayout.clear(numGlyphs);
       
  2121 
       
  2122     *this = newLayout;
       
  2123 }
       
  2124 
       
  2125 void QTextEngine::freeMemory()
       
  2126 {
       
  2127     if (!stackEngine) {
       
  2128         delete layoutData;
       
  2129         layoutData = 0;
       
  2130     } else {
       
  2131         layoutData->used = 0;
       
  2132         layoutData->hasBidi = false;
       
  2133         layoutData->inLayout = false;
       
  2134         layoutData->haveCharAttributes = false;
       
  2135     }
       
  2136     for (int i = 0; i < lines.size(); ++i) {
       
  2137         lines[i].justified = 0;
       
  2138         lines[i].gridfitted = 0;
       
  2139     }
       
  2140 }
       
  2141 
       
  2142 int QTextEngine::formatIndex(const QScriptItem *si) const
       
  2143 {
       
  2144     if (specialData && !specialData->resolvedFormatIndices.isEmpty())
       
  2145         return specialData->resolvedFormatIndices.at(si - &layoutData->items[0]);
       
  2146     QTextDocumentPrivate *p = block.docHandle();
       
  2147     if (!p)
       
  2148         return -1;
       
  2149     int pos = si->position;
       
  2150     if (specialData && si->position >= specialData->preeditPosition) {
       
  2151         if (si->position < specialData->preeditPosition + specialData->preeditText.length())
       
  2152             pos = qMax(specialData->preeditPosition - 1, 0);
       
  2153         else
       
  2154             pos -= specialData->preeditText.length();
       
  2155     }
       
  2156     QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
       
  2157     return it.value()->format;
       
  2158 }
       
  2159 
       
  2160 
       
  2161 QTextCharFormat QTextEngine::format(const QScriptItem *si) const
       
  2162 {
       
  2163     QTextCharFormat format;
       
  2164     const QTextFormatCollection *formats = 0;
       
  2165     if (block.docHandle()) {
       
  2166         formats = this->formats();
       
  2167         format = formats->charFormat(formatIndex(si));
       
  2168     }
       
  2169     if (specialData && specialData->resolvedFormatIndices.isEmpty()) {
       
  2170         int end = si->position + length(si);
       
  2171         for (int i = 0; i < specialData->addFormats.size(); ++i) {
       
  2172             const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
       
  2173             if (r.start <= si->position && r.start + r.length >= end) {
       
  2174                 if (!specialData->addFormatIndices.isEmpty())
       
  2175                     format.merge(formats->format(specialData->addFormatIndices.at(i)));
       
  2176                 else
       
  2177                     format.merge(r.format);
       
  2178             }
       
  2179         }
       
  2180     }
       
  2181     return format;
       
  2182 }
       
  2183 
       
  2184 void QTextEngine::addRequiredBoundaries() const
       
  2185 {
       
  2186     if (specialData) {
       
  2187         for (int i = 0; i < specialData->addFormats.size(); ++i) {
       
  2188             const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
       
  2189             setBoundary(r.start);
       
  2190             setBoundary(r.start + r.length);
       
  2191             //qDebug("adding boundaries %d %d", r.start, r.start+r.length);
       
  2192         }
       
  2193     }
       
  2194 }
       
  2195 
       
  2196 bool QTextEngine::atWordSeparator(int position) const
       
  2197 {
       
  2198     const QChar c = layoutData->string.at(position);
       
  2199     switch (c.toLatin1()) {
       
  2200     case '.':
       
  2201     case ',':
       
  2202     case '?':
       
  2203     case '!':
       
  2204     case ':':
       
  2205     case ';':
       
  2206     case '-':
       
  2207     case '<':
       
  2208     case '>':
       
  2209     case '[':
       
  2210     case ']':
       
  2211     case '(':
       
  2212     case ')':
       
  2213     case '{':
       
  2214     case '}':
       
  2215     case '=':
       
  2216     case '/':
       
  2217     case '+':
       
  2218     case '%':
       
  2219     case '&':
       
  2220     case '^':
       
  2221     case '*':
       
  2222     case '\'':
       
  2223     case '"':
       
  2224     case '~':
       
  2225     case '|':
       
  2226         return true;
       
  2227     default:
       
  2228         return false;
       
  2229     }
       
  2230 }
       
  2231 
       
  2232 bool QTextEngine::atSpace(int position) const
       
  2233 {
       
  2234     const QChar c = layoutData->string.at(position);
       
  2235 
       
  2236     return c == QLatin1Char(' ')
       
  2237         || c == QChar::Nbsp
       
  2238         || c == QChar::LineSeparator
       
  2239         || c == QLatin1Char('\t')
       
  2240         ;
       
  2241 }
       
  2242 
       
  2243 
       
  2244 void QTextEngine::indexAdditionalFormats()
       
  2245 {
       
  2246     if (!block.docHandle())
       
  2247         return;
       
  2248 
       
  2249     specialData->addFormatIndices.resize(specialData->addFormats.count());
       
  2250     QTextFormatCollection * const formats = this->formats();
       
  2251 
       
  2252     for (int i = 0; i < specialData->addFormats.count(); ++i) {
       
  2253         specialData->addFormatIndices[i] = formats->indexForFormat(specialData->addFormats.at(i).format);
       
  2254         specialData->addFormats[i].format = QTextCharFormat();
       
  2255     }
       
  2256 }
       
  2257 
       
  2258 /* These two helper functions are used to determine whether we need to insert a ZWJ character
       
  2259    between the text that gets truncated and the ellipsis. This is important to get
       
  2260    correctly shaped results for arabic text.
       
  2261 */
       
  2262 static bool nextCharJoins(const QString &string, int pos)
       
  2263 {
       
  2264     while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
       
  2265         ++pos;
       
  2266     if (pos == string.length())
       
  2267         return false;
       
  2268     return string.at(pos).joining() != QChar::OtherJoining;
       
  2269 }
       
  2270 
       
  2271 static bool prevCharJoins(const QString &string, int pos)
       
  2272 {
       
  2273     while (pos > 0 && string.at(pos - 1).category() == QChar::Mark_NonSpacing)
       
  2274         --pos;
       
  2275     if (pos == 0)
       
  2276         return false;
       
  2277     return (string.at(pos - 1).joining() == QChar::Dual || string.at(pos - 1).joining() == QChar::Center);
       
  2278 }
       
  2279 
       
  2280 QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const
       
  2281 {
       
  2282 //    qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
       
  2283 
       
  2284     if (flags & Qt::TextShowMnemonic) {
       
  2285         itemize();
       
  2286         for (int i = 0; i < layoutData->items.size(); ++i) {
       
  2287             QScriptItem &si = layoutData->items[i];
       
  2288             if (!si.num_glyphs)
       
  2289                 shape(i);
       
  2290 
       
  2291             HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
       
  2292             unsigned short *logClusters = this->logClusters(&si);
       
  2293             QGlyphLayout glyphs = shapedGlyphs(&si);
       
  2294 
       
  2295             const int end = si.position + length(&si);
       
  2296             for (int i = si.position; i < end - 1; ++i)
       
  2297                 if (layoutData->string.at(i) == QLatin1Char('&')) {
       
  2298                     const int gp = logClusters[i - si.position];
       
  2299                     glyphs.attributes[gp].dontPrint = true;
       
  2300                     attributes[i + 1].charStop = false;
       
  2301                     attributes[i + 1].whiteSpace = false;
       
  2302                     attributes[i + 1].lineBreakType = HB_NoBreak;
       
  2303                     if (i < end - 1
       
  2304                             && layoutData->string.at(i + 1) == QLatin1Char('&'))
       
  2305                         ++i;
       
  2306                 }
       
  2307         }
       
  2308     }
       
  2309 
       
  2310     validate();
       
  2311 
       
  2312     if (mode == Qt::ElideNone
       
  2313         || this->width(0, layoutData->string.length()) <= width
       
  2314         || layoutData->string.length() <= 1)
       
  2315         return layoutData->string;
       
  2316 
       
  2317     QFixed ellipsisWidth;
       
  2318     QString ellipsisText;
       
  2319     {
       
  2320         QChar ellipsisChar(0x2026);
       
  2321 
       
  2322         QFontEngine *fe = fnt.d->engineForScript(QUnicodeTables::Common);
       
  2323 
       
  2324         QGlyphLayoutArray<1> ellipsisGlyph;
       
  2325         {
       
  2326             QFontEngine *feForEllipsis = (fe->type() == QFontEngine::Multi)
       
  2327                 ? static_cast<QFontEngineMulti *>(fe)->engine(0)
       
  2328                 : fe;
       
  2329 
       
  2330             if (feForEllipsis->type() == QFontEngine::Mac)
       
  2331                 feForEllipsis = fe;
       
  2332 
       
  2333             // the lookup can be really slow when we use XLFD fonts
       
  2334             if (feForEllipsis->type() != QFontEngine::XLFD
       
  2335                 && feForEllipsis->canRender(&ellipsisChar, 1)) {
       
  2336                     int nGlyphs = 1;
       
  2337                     feForEllipsis->stringToCMap(&ellipsisChar, 1, &ellipsisGlyph, &nGlyphs, 0);
       
  2338                 }
       
  2339         }
       
  2340 
       
  2341         if (ellipsisGlyph.glyphs[0]) {
       
  2342             ellipsisWidth = ellipsisGlyph.advances_x[0];
       
  2343             ellipsisText = ellipsisChar;
       
  2344         } else {
       
  2345             QString dotDotDot(QLatin1String("..."));
       
  2346 
       
  2347             QGlyphLayoutArray<3> glyphs;
       
  2348             int nGlyphs = 3;
       
  2349             if (!fe->stringToCMap(dotDotDot.constData(), 3, &glyphs, &nGlyphs, 0))
       
  2350                 // should never happen...
       
  2351                 return layoutData->string;
       
  2352             for (int i = 0; i < nGlyphs; ++i)
       
  2353                 ellipsisWidth += glyphs.advances_x[i];
       
  2354             ellipsisText = dotDotDot;
       
  2355         }
       
  2356     }
       
  2357 
       
  2358     const QFixed availableWidth = width - ellipsisWidth;
       
  2359     if (availableWidth < 0)
       
  2360         return QString();
       
  2361 
       
  2362     const HB_CharAttributes *attributes = this->attributes();
       
  2363 
       
  2364     if (mode == Qt::ElideRight) {
       
  2365         QFixed currentWidth;
       
  2366         int pos = 0;
       
  2367         int nextBreak = 0;
       
  2368 
       
  2369         do {
       
  2370             pos = nextBreak;
       
  2371 
       
  2372             ++nextBreak;
       
  2373             while (nextBreak < layoutData->string.length() && !attributes[nextBreak].charStop)
       
  2374                 ++nextBreak;
       
  2375 
       
  2376             currentWidth += this->width(pos, nextBreak - pos);
       
  2377         } while (nextBreak < layoutData->string.length()
       
  2378                  && currentWidth < availableWidth);
       
  2379 
       
  2380         if (nextCharJoins(layoutData->string, pos))
       
  2381             ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
       
  2382 
       
  2383         return layoutData->string.left(pos) + ellipsisText;
       
  2384     } else if (mode == Qt::ElideLeft) {
       
  2385         QFixed currentWidth;
       
  2386         int pos = layoutData->string.length();
       
  2387         int nextBreak = layoutData->string.length();
       
  2388 
       
  2389         do {
       
  2390             pos = nextBreak;
       
  2391 
       
  2392             --nextBreak;
       
  2393             while (nextBreak > 0 && !attributes[nextBreak].charStop)
       
  2394                 --nextBreak;
       
  2395 
       
  2396             currentWidth += this->width(nextBreak, pos - nextBreak);
       
  2397         } while (nextBreak > 0
       
  2398                  && currentWidth < availableWidth);
       
  2399 
       
  2400         if (prevCharJoins(layoutData->string, pos))
       
  2401             ellipsisText.append(QChar(0x200d) /* ZWJ */);
       
  2402 
       
  2403         return ellipsisText + layoutData->string.mid(pos);
       
  2404     } else if (mode == Qt::ElideMiddle) {
       
  2405         QFixed leftWidth;
       
  2406         QFixed rightWidth;
       
  2407 
       
  2408         int leftPos = 0;
       
  2409         int nextLeftBreak = 0;
       
  2410 
       
  2411         int rightPos = layoutData->string.length();
       
  2412         int nextRightBreak = layoutData->string.length();
       
  2413 
       
  2414         do {
       
  2415             leftPos = nextLeftBreak;
       
  2416             rightPos = nextRightBreak;
       
  2417 
       
  2418             ++nextLeftBreak;
       
  2419             while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].charStop)
       
  2420                 ++nextLeftBreak;
       
  2421 
       
  2422             --nextRightBreak;
       
  2423             while (nextRightBreak > 0 && !attributes[nextRightBreak].charStop)
       
  2424                 --nextRightBreak;
       
  2425 
       
  2426             leftWidth += this->width(leftPos, nextLeftBreak - leftPos);
       
  2427             rightWidth += this->width(nextRightBreak, rightPos - nextRightBreak);
       
  2428         } while (nextLeftBreak < layoutData->string.length()
       
  2429                  && nextRightBreak > 0
       
  2430                  && leftWidth + rightWidth < availableWidth);
       
  2431 
       
  2432         if (nextCharJoins(layoutData->string, leftPos))
       
  2433             ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
       
  2434         if (prevCharJoins(layoutData->string, rightPos))
       
  2435             ellipsisText.append(QChar(0x200d) /* ZWJ */);
       
  2436 
       
  2437         return layoutData->string.left(leftPos) + ellipsisText + layoutData->string.mid(rightPos);
       
  2438     }
       
  2439 
       
  2440     return layoutData->string;
       
  2441 }
       
  2442 
       
  2443 void QTextEngine::setBoundary(int strPos) const
       
  2444 {
       
  2445     if (strPos <= 0 || strPos >= layoutData->string.length())
       
  2446         return;
       
  2447 
       
  2448     int itemToSplit = 0;
       
  2449     while (itemToSplit < layoutData->items.size() && layoutData->items[itemToSplit].position <= strPos)
       
  2450         itemToSplit++;
       
  2451     itemToSplit--;
       
  2452     if (layoutData->items[itemToSplit].position == strPos) {
       
  2453         // already a split at the requested position
       
  2454         return;
       
  2455     }
       
  2456     splitItem(itemToSplit, strPos - layoutData->items[itemToSplit].position);
       
  2457 }
       
  2458 
       
  2459 void QTextEngine::splitItem(int item, int pos) const
       
  2460 {
       
  2461     if (pos <= 0)
       
  2462         return;
       
  2463 
       
  2464     layoutData->items.insert(item + 1, QScriptItem(layoutData->items[item]));
       
  2465     QScriptItem &oldItem = layoutData->items[item];
       
  2466     QScriptItem &newItem = layoutData->items[item+1];
       
  2467     newItem.position += pos;
       
  2468 
       
  2469     if (oldItem.num_glyphs) {
       
  2470         // already shaped, break glyphs aswell
       
  2471         int breakGlyph = logClusters(&oldItem)[pos];
       
  2472 
       
  2473         newItem.num_glyphs = oldItem.num_glyphs - breakGlyph;
       
  2474         oldItem.num_glyphs = breakGlyph;
       
  2475         newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph;
       
  2476 
       
  2477         for (int i = 0; i < newItem.num_glyphs; i++)
       
  2478             logClusters(&newItem)[i] -= breakGlyph;
       
  2479 
       
  2480         QFixed w = 0;
       
  2481         const QGlyphLayout g = shapedGlyphs(&oldItem);
       
  2482         for(int j = 0; j < breakGlyph; ++j)
       
  2483             w += g.advances_x[j];
       
  2484 
       
  2485         newItem.width = oldItem.width - w;
       
  2486         oldItem.width = w;
       
  2487     }
       
  2488 
       
  2489 //     qDebug("split at position %d itempos=%d", pos, item);
       
  2490 }
       
  2491 
       
  2492 extern int qt_defaultDpiY();
       
  2493 
       
  2494 QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
       
  2495 {
       
  2496     const QScriptItem &si = layoutData->items[item];
       
  2497 
       
  2498     QFixed dpiScale = 1;
       
  2499     if (block.docHandle() && block.docHandle()->layout()) {
       
  2500         QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  2501         if (pdev)
       
  2502             dpiScale = QFixed::fromReal(pdev->logicalDpiY() / qreal(qt_defaultDpiY()));
       
  2503     } else {
       
  2504         dpiScale = QFixed::fromReal(fnt.d->dpi / qreal(qt_defaultDpiY()));
       
  2505     }
       
  2506 
       
  2507     QList<QTextOption::Tab> tabArray = option.tabs();
       
  2508     if (!tabArray.isEmpty()) {
       
  2509         if (option.textDirection() == Qt::RightToLeft) { // rebase the tabArray positions.
       
  2510             QList<QTextOption::Tab> newTabs;
       
  2511             QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
       
  2512             while(iter != tabArray.end()) {
       
  2513                 QTextOption::Tab tab = *iter;
       
  2514                 if (tab.type == QTextOption::LeftTab)
       
  2515                     tab.type = QTextOption::RightTab;
       
  2516                 else if (tab.type == QTextOption::RightTab)
       
  2517                     tab.type = QTextOption::LeftTab;
       
  2518                 newTabs << tab;
       
  2519                 ++iter;
       
  2520             }
       
  2521             tabArray = newTabs;
       
  2522         }
       
  2523         for (int i = 0; i < tabArray.size(); ++i) {
       
  2524             QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
       
  2525             if (tab > x) {  // this is the tab we need.
       
  2526                 QTextOption::Tab tabSpec = tabArray[i];
       
  2527                 int tabSectionEnd = layoutData->string.count();
       
  2528                 if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
       
  2529                     // find next tab to calculate the width required.
       
  2530                     tab = QFixed::fromReal(tabSpec.position);
       
  2531                     for (int i=item + 1; i < layoutData->items.count(); i++) {
       
  2532                         const QScriptItem &item = layoutData->items[i];
       
  2533                         if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
       
  2534                             tabSectionEnd = item.position;
       
  2535                             break;
       
  2536                         }
       
  2537                     }
       
  2538                 }
       
  2539                 else if (tabSpec.type == QTextOption::DelimiterTab)
       
  2540                     // find delimitor character to calculate the width required
       
  2541                     tabSectionEnd = qMax(si.position, layoutData->string.indexOf(tabSpec.delimiter, si.position) + 1);
       
  2542 
       
  2543                 if (tabSectionEnd > si.position) {
       
  2544                     QFixed length;
       
  2545                     // Calculate the length of text between this tab and the tabSectionEnd
       
  2546                     for (int i=item; i < layoutData->items.count(); i++) {
       
  2547                         QScriptItem &item = layoutData->items[i];
       
  2548                         if (item.position > tabSectionEnd || item.position <= si.position)
       
  2549                             continue;
       
  2550                         shape(i); // first, lets make sure relevant text is already shaped
       
  2551                         QGlyphLayout glyphs = this->shapedGlyphs(&item);
       
  2552                         const int end = qMin(item.position + item.num_glyphs, tabSectionEnd) - item.position;
       
  2553                         for (int i=0; i < end; i++)
       
  2554                             length += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
       
  2555                         if (end + item.position == tabSectionEnd && tabSpec.type == QTextOption::DelimiterTab) // remove half of matching char
       
  2556                             length -= glyphs.advances_x[end] / 2 * !glyphs.attributes[end].dontPrint;
       
  2557                     }
       
  2558 
       
  2559                     switch (tabSpec.type) {
       
  2560                     case QTextOption::CenterTab:
       
  2561                         length /= 2;
       
  2562                         // fall through
       
  2563                     case QTextOption::DelimiterTab:
       
  2564                         // fall through
       
  2565                     case QTextOption::RightTab:
       
  2566                         tab = QFixed::fromReal(tabSpec.position) * dpiScale - length;
       
  2567                         if (tab < 0) // default to tab taking no space
       
  2568                             return QFixed();
       
  2569                         break;
       
  2570                     case QTextOption::LeftTab:
       
  2571                         break;
       
  2572                     }
       
  2573                 }
       
  2574                 return tab - x;
       
  2575             }
       
  2576         }
       
  2577     }
       
  2578     QFixed tab = QFixed::fromReal(option.tabStop());
       
  2579     if (tab <= 0)
       
  2580         tab = 80; // default
       
  2581     tab *= dpiScale;
       
  2582     QFixed nextTabPos = ((x / tab).truncate() + 1) * tab;
       
  2583     QFixed tabWidth = nextTabPos - x;
       
  2584 
       
  2585     return tabWidth;
       
  2586 }
       
  2587 
       
  2588 void QTextEngine::resolveAdditionalFormats() const
       
  2589 {
       
  2590     if (!specialData || specialData->addFormats.isEmpty()
       
  2591         || !block.docHandle()
       
  2592         || !specialData->resolvedFormatIndices.isEmpty())
       
  2593         return;
       
  2594 
       
  2595     QTextFormatCollection *collection = this->formats();
       
  2596 
       
  2597     specialData->resolvedFormatIndices.clear();
       
  2598     QVector<int> indices(layoutData->items.count());
       
  2599     for (int i = 0; i < layoutData->items.count(); ++i) {
       
  2600         QTextCharFormat f = format(&layoutData->items.at(i));
       
  2601         indices[i] = collection->indexForFormat(f);
       
  2602     }
       
  2603     specialData->resolvedFormatIndices = indices;
       
  2604 }
       
  2605 
       
  2606 QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
       
  2607     : _layoutData(string, _memory, MemSize)
       
  2608 {
       
  2609     fnt = f;
       
  2610     text = string;
       
  2611     stackEngine = true;
       
  2612     layoutData = &_layoutData;
       
  2613 }
       
  2614 
       
  2615 QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format)
       
  2616     : justified(false), underlineStyle(QTextCharFormat::NoUnderline), charFormat(format),
       
  2617       num_chars(0), chars(0), logClusters(0), f(0), fontEngine(0)
       
  2618 {
       
  2619     // explicitly initialize flags so that initFontAttributes can be called
       
  2620     // multiple times on the same TextItem
       
  2621     flags = 0;
       
  2622     if (si.analysis.bidiLevel %2)
       
  2623         flags |= QTextItem::RightToLeft;
       
  2624     ascent = si.ascent;
       
  2625     descent = si.descent;
       
  2626     f = font;
       
  2627     fontEngine = f->d->engineForScript(si.analysis.script);
       
  2628     Q_ASSERT(fontEngine);
       
  2629 
       
  2630     if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
       
  2631         underlineStyle = format.underlineStyle();
       
  2632     } else if (format.boolProperty(QTextFormat::FontUnderline)
       
  2633                || f->d->underline) {
       
  2634         underlineStyle = QTextCharFormat::SingleUnderline;
       
  2635     }
       
  2636 
       
  2637     // compat
       
  2638     if (underlineStyle == QTextCharFormat::SingleUnderline)
       
  2639         flags |= QTextItem::Underline;
       
  2640 
       
  2641     if (f->d->overline || format.fontOverline())
       
  2642         flags |= QTextItem::Overline;
       
  2643     if (f->d->strikeOut || format.fontStrikeOut())
       
  2644         flags |= QTextItem::StrikeOut;
       
  2645 }
       
  2646 
       
  2647 QTextItemInt QTextItemInt::midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const
       
  2648 {
       
  2649     QTextItemInt ti = *this;
       
  2650     const int end = firstGlyphIndex + numGlyphs;
       
  2651     ti.glyphs = glyphs.mid(firstGlyphIndex, numGlyphs);
       
  2652     ti.fontEngine = fontEngine;
       
  2653 
       
  2654     if (logClusters && chars) {
       
  2655         const int logClusterOffset = logClusters[0];
       
  2656         while (logClusters[ti.chars - chars] - logClusterOffset < firstGlyphIndex)
       
  2657             ++ti.chars;
       
  2658 
       
  2659         ti.logClusters += (ti.chars - chars);
       
  2660 
       
  2661         ti.num_chars = 0;
       
  2662         int char_start = ti.chars - chars;
       
  2663         while (char_start + ti.num_chars < num_chars && ti.logClusters[ti.num_chars] - logClusterOffset < end)
       
  2664             ++ti.num_chars;
       
  2665     }
       
  2666     return ti;
       
  2667 }
       
  2668 
       
  2669 
       
  2670 QTransform qt_true_matrix(qreal w, qreal h, QTransform x)
       
  2671 {
       
  2672     QRectF rect = x.mapRect(QRectF(0, 0, w, h));
       
  2673     return x * QTransform::fromTranslate(-rect.x(), -rect.y());
       
  2674 }
       
  2675 
       
  2676 
       
  2677 glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
       
  2678 {
       
  2679     if (matrix.type() < QTransform::TxTranslate)
       
  2680         return *this;
       
  2681 
       
  2682     glyph_metrics_t m = *this;
       
  2683 
       
  2684     qreal w = width.toReal();
       
  2685     qreal h = height.toReal();
       
  2686     QTransform xform = qt_true_matrix(w, h, matrix);
       
  2687 
       
  2688     QRectF rect(0, 0, w, h);
       
  2689     rect = xform.mapRect(rect);
       
  2690     m.width = QFixed::fromReal(rect.width());
       
  2691     m.height = QFixed::fromReal(rect.height());
       
  2692 
       
  2693     QLineF l = xform.map(QLineF(x.toReal(), y.toReal(), xoff.toReal(), yoff.toReal()));
       
  2694 
       
  2695     m.x = QFixed::fromReal(l.x1());
       
  2696     m.y = QFixed::fromReal(l.y1());
       
  2697 
       
  2698     // The offset is relative to the baseline which is why we use dx/dy of the line
       
  2699     m.xoff = QFixed::fromReal(l.dx());
       
  2700     m.yoff = QFixed::fromReal(l.dy());
       
  2701 
       
  2702     return m;
       
  2703 }
       
  2704 
       
  2705 
       
  2706 QT_END_NAMESPACE