util/src/gui/text/qtextengine.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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(reinterpret_cast<const ushort *>(m_string.unicode()), 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, &si.leading);
       
  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, &si.leading);
       
  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/leading of the script item for the tab
       
  1354         fontEngine(layoutData->items[item],
       
  1355                    &layoutData->items[item].ascent,
       
  1356                    &layoutData->items[item].descent,
       
  1357                    &layoutData->items[item].leading);
       
  1358     } else {
       
  1359         shapeText(item);
       
  1360     }
       
  1361 }
       
  1362 
       
  1363 void QTextEngine::invalidate()
       
  1364 {
       
  1365     freeMemory();
       
  1366     minWidth = 0;
       
  1367     maxWidth = 0;
       
  1368     if (specialData)
       
  1369         specialData->resolvedFormatIndices.clear();
       
  1370 }
       
  1371 
       
  1372 void QTextEngine::clearLineData()
       
  1373 {
       
  1374     lines.clear();
       
  1375 }
       
  1376 
       
  1377 void QTextEngine::validate() const
       
  1378 {
       
  1379     if (layoutData)
       
  1380         return;
       
  1381     layoutData = new LayoutData();
       
  1382     if (block.docHandle()) {
       
  1383         layoutData->string = block.text();
       
  1384         if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
       
  1385             layoutData->string += QLatin1Char(block.next().isValid() ? 0xb6 : 0x20);
       
  1386     } else {
       
  1387         layoutData->string = text;
       
  1388     }
       
  1389     if (specialData && specialData->preeditPosition != -1)
       
  1390         layoutData->string.insert(specialData->preeditPosition, specialData->preeditText);
       
  1391 }
       
  1392 
       
  1393 void QTextEngine::itemize() const
       
  1394 {
       
  1395     validate();
       
  1396     if (layoutData->items.size())
       
  1397         return;
       
  1398 
       
  1399     int length = layoutData->string.length();
       
  1400     if (!length)
       
  1401         return;
       
  1402 #if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
       
  1403     // ATSUI requires RTL flags to correctly identify the character stops.
       
  1404     bool ignore = false;
       
  1405 #else
       
  1406     bool ignore = ignoreBidi;
       
  1407 #endif
       
  1408     if (!ignore && option.textDirection() == Qt::LeftToRight) {
       
  1409         ignore = true;
       
  1410         const QChar *start = layoutData->string.unicode();
       
  1411         const QChar * const end = start + length;
       
  1412         while (start < end) {
       
  1413             if (start->unicode() >= 0x590) {
       
  1414                 ignore = false;
       
  1415                 break;
       
  1416             }
       
  1417             ++start;
       
  1418         }
       
  1419     }
       
  1420 
       
  1421     QVarLengthArray<QScriptAnalysis, 4096> scriptAnalysis(length);
       
  1422     QScriptAnalysis *analysis = scriptAnalysis.data();
       
  1423 
       
  1424     QBidiControl control(option.textDirection() == Qt::RightToLeft);
       
  1425 
       
  1426     if (ignore) {
       
  1427         memset(analysis, 0, length*sizeof(QScriptAnalysis));
       
  1428         if (option.textDirection() == Qt::RightToLeft) {
       
  1429             for (int i = 0; i < length; ++i)
       
  1430                 analysis[i].bidiLevel = 1;
       
  1431             layoutData->hasBidi = true;
       
  1432         }
       
  1433     } else {
       
  1434         layoutData->hasBidi = bidiItemize(const_cast<QTextEngine *>(this), analysis, control);
       
  1435     }
       
  1436 
       
  1437     const ushort *uc = reinterpret_cast<const ushort *>(layoutData->string.unicode());
       
  1438     const ushort *e = uc + length;
       
  1439     int lastScript = QUnicodeTables::Common;
       
  1440     while (uc < e) {
       
  1441         int script = QUnicodeTables::script(*uc);
       
  1442         if (script == QUnicodeTables::Inherited)
       
  1443             script = lastScript;
       
  1444         analysis->flags = QScriptAnalysis::None;
       
  1445         if (*uc == QChar::ObjectReplacementCharacter) {
       
  1446             if (analysis->bidiLevel % 2)
       
  1447                 --analysis->bidiLevel;
       
  1448             analysis->script = QUnicodeTables::Common;
       
  1449             analysis->flags = QScriptAnalysis::Object;
       
  1450         } else if (*uc == QChar::LineSeparator) {
       
  1451             if (analysis->bidiLevel % 2)
       
  1452                 --analysis->bidiLevel;
       
  1453             analysis->script = QUnicodeTables::Common;
       
  1454             analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
       
  1455             if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
       
  1456                 *const_cast<ushort*>(uc) = 0x21B5; // visual line separator
       
  1457         } else if (*uc == 9) {
       
  1458             analysis->script = QUnicodeTables::Common;
       
  1459             analysis->flags = QScriptAnalysis::Tab;
       
  1460             analysis->bidiLevel = control.baseLevel();
       
  1461         } else if ((*uc == 32 || *uc == QChar::Nbsp)
       
  1462                    && (option.flags() & QTextOption::ShowTabsAndSpaces)) {
       
  1463             analysis->script = QUnicodeTables::Common;
       
  1464             analysis->flags = QScriptAnalysis::Space;
       
  1465             analysis->bidiLevel = control.baseLevel();
       
  1466         } else {
       
  1467             analysis->script = script;
       
  1468         }
       
  1469         lastScript = analysis->script;
       
  1470         ++uc;
       
  1471         ++analysis;
       
  1472     }
       
  1473     if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
       
  1474         (analysis-1)->flags = QScriptAnalysis::LineOrParagraphSeparator; // to exclude it from width
       
  1475     }
       
  1476 
       
  1477     Itemizer itemizer(layoutData->string, scriptAnalysis.data(), layoutData->items);
       
  1478 
       
  1479     const QTextDocumentPrivate *p = block.docHandle();
       
  1480     if (p) {
       
  1481         SpecialData *s = specialData;
       
  1482 
       
  1483         QTextDocumentPrivate::FragmentIterator it = p->find(block.position());
       
  1484         QTextDocumentPrivate::FragmentIterator end = p->find(block.position() + block.length() - 1); // -1 to omit the block separator char
       
  1485         int format = it.value()->format;
       
  1486 
       
  1487         int prevPosition = 0;
       
  1488         int position = prevPosition;
       
  1489         while (1) {
       
  1490             const QTextFragmentData * const frag = it.value();
       
  1491             if (it == end || format != frag->format) {
       
  1492                 if (s && position >= s->preeditPosition) {
       
  1493                     position += s->preeditText.length();
       
  1494                     s = 0;
       
  1495                 }
       
  1496                 Q_ASSERT(position <= length);
       
  1497                 itemizer.generate(prevPosition, position - prevPosition,
       
  1498                     formats()->charFormat(format).fontCapitalization());
       
  1499                 if (it == end) {
       
  1500                     if (position < length)
       
  1501                         itemizer.generate(position, length - position,
       
  1502                                           formats()->charFormat(format).fontCapitalization());
       
  1503                     break;
       
  1504                 }
       
  1505                 format = frag->format;
       
  1506                 prevPosition = position;
       
  1507             }
       
  1508             position += frag->size_array[0];
       
  1509             ++it;
       
  1510         }
       
  1511     } else {
       
  1512         itemizer.generate(0, length, static_cast<QFont::Capitalization> (fnt.d->capital));
       
  1513     }
       
  1514 
       
  1515     addRequiredBoundaries();
       
  1516     resolveAdditionalFormats();
       
  1517 }
       
  1518 
       
  1519 int QTextEngine::findItem(int strPos) const
       
  1520 {
       
  1521     itemize();
       
  1522 
       
  1523     // ##### use binary search
       
  1524     int item;
       
  1525     for (item = layoutData->items.size()-1; item > 0; --item) {
       
  1526         if (layoutData->items[item].position <= strPos)
       
  1527             break;
       
  1528     }
       
  1529     return item;
       
  1530 }
       
  1531 
       
  1532 QFixed QTextEngine::width(int from, int len) const
       
  1533 {
       
  1534     itemize();
       
  1535 
       
  1536     QFixed w = 0;
       
  1537 
       
  1538 //     qDebug("QTextEngine::width(from = %d, len = %d), numItems=%d, strleng=%d", from,  len, items.size(), string.length());
       
  1539     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1540         const QScriptItem *si = layoutData->items.constData() + i;
       
  1541         int pos = si->position;
       
  1542         int ilen = length(i);
       
  1543 //          qDebug("item %d: from %d len %d", i, pos, ilen);
       
  1544         if (pos >= from + len)
       
  1545             break;
       
  1546         if (pos + ilen > from) {
       
  1547             if (!si->num_glyphs)
       
  1548                 shape(i);
       
  1549 
       
  1550             if (si->analysis.flags == QScriptAnalysis::Object) {
       
  1551                 w += si->width;
       
  1552                 continue;
       
  1553             } else if (si->analysis.flags == QScriptAnalysis::Tab) {
       
  1554                 w += calculateTabWidth(i, w);
       
  1555                 continue;
       
  1556             }
       
  1557 
       
  1558 
       
  1559             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1560             unsigned short *logClusters = this->logClusters(si);
       
  1561 
       
  1562 //             fprintf(stderr, "  logclusters:");
       
  1563 //             for (int k = 0; k < ilen; k++)
       
  1564 //                 fprintf(stderr, " %d", logClusters[k]);
       
  1565 //             fprintf(stderr, "\n");
       
  1566             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1567             int charFrom = from - pos;
       
  1568             if (charFrom < 0)
       
  1569                 charFrom = 0;
       
  1570             int glyphStart = logClusters[charFrom];
       
  1571             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1572                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1573                     charFrom++;
       
  1574             if (charFrom < ilen) {
       
  1575                 glyphStart = logClusters[charFrom];
       
  1576                 int charEnd = from + len - 1 - pos;
       
  1577                 if (charEnd >= ilen)
       
  1578                     charEnd = ilen-1;
       
  1579                 int glyphEnd = logClusters[charEnd];
       
  1580                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1581                     charEnd++;
       
  1582                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1583 
       
  1584 //                 qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd);
       
  1585                 for (int i = glyphStart; i < glyphEnd; i++)
       
  1586                     w += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
       
  1587             }
       
  1588         }
       
  1589     }
       
  1590 //     qDebug("   --> w= %d ", w);
       
  1591     return w;
       
  1592 }
       
  1593 
       
  1594 glyph_metrics_t QTextEngine::boundingBox(int from,  int len) const
       
  1595 {
       
  1596     itemize();
       
  1597 
       
  1598     glyph_metrics_t gm;
       
  1599 
       
  1600     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1601         const QScriptItem *si = layoutData->items.constData() + i;
       
  1602         QFontEngine *fe = fontEngine(*si);
       
  1603 
       
  1604         int pos = si->position;
       
  1605         int ilen = length(i);
       
  1606         if (pos > from + len)
       
  1607             break;
       
  1608         if (pos + ilen > from) {
       
  1609             if (!si->num_glyphs)
       
  1610                 shape(i);
       
  1611 
       
  1612             if (si->analysis.flags == QScriptAnalysis::Object) {
       
  1613                 gm.width += si->width;
       
  1614                 continue;
       
  1615             } else if (si->analysis.flags == QScriptAnalysis::Tab) {
       
  1616                 gm.width += calculateTabWidth(i, gm.width);
       
  1617                 continue;
       
  1618             }
       
  1619 
       
  1620             unsigned short *logClusters = this->logClusters(si);
       
  1621             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1622 
       
  1623             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1624             int charFrom = from - pos;
       
  1625             if (charFrom < 0)
       
  1626                 charFrom = 0;
       
  1627             int glyphStart = logClusters[charFrom];
       
  1628             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1629                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1630                     charFrom++;
       
  1631             if (charFrom < ilen) {
       
  1632                 glyphStart = logClusters[charFrom];
       
  1633                 int charEnd = from + len - 1 - pos;
       
  1634                 if (charEnd >= ilen)
       
  1635                     charEnd = ilen-1;
       
  1636                 int glyphEnd = logClusters[charEnd];
       
  1637                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1638                     charEnd++;
       
  1639                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1640                 if (glyphStart <= glyphEnd ) {
       
  1641                     glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
       
  1642                     gm.x = qMin(gm.x, m.x + gm.xoff);
       
  1643                     gm.y = qMin(gm.y, m.y + gm.yoff);
       
  1644                     gm.width = qMax(gm.width, m.width+gm.xoff);
       
  1645                     gm.height = qMax(gm.height, m.height+gm.yoff);
       
  1646                     gm.xoff += m.xoff;
       
  1647                     gm.yoff += m.yoff;
       
  1648                 }
       
  1649             }
       
  1650 
       
  1651             glyph_t glyph = glyphs.glyphs[logClusters[pos + ilen - 1]];
       
  1652             glyph_metrics_t gi = fe->boundingBox(glyph);
       
  1653             if (gi.isValid())
       
  1654                 gm.width -= qRound(gi.xoff - gi.x - gi.width);
       
  1655         }
       
  1656     }
       
  1657     return gm;
       
  1658 }
       
  1659 
       
  1660 glyph_metrics_t QTextEngine::tightBoundingBox(int from,  int len) const
       
  1661 {
       
  1662     itemize();
       
  1663 
       
  1664     glyph_metrics_t gm;
       
  1665 
       
  1666     for (int i = 0; i < layoutData->items.size(); i++) {
       
  1667         const QScriptItem *si = layoutData->items.constData() + i;
       
  1668         int pos = si->position;
       
  1669         int ilen = length(i);
       
  1670         if (pos > from + len)
       
  1671             break;
       
  1672         if (pos + len > from) {
       
  1673             if (!si->num_glyphs)
       
  1674                 shape(i);
       
  1675             unsigned short *logClusters = this->logClusters(si);
       
  1676             QGlyphLayout glyphs = shapedGlyphs(si);
       
  1677 
       
  1678             // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
       
  1679             int charFrom = from - pos;
       
  1680             if (charFrom < 0)
       
  1681                 charFrom = 0;
       
  1682             int glyphStart = logClusters[charFrom];
       
  1683             if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
       
  1684                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
       
  1685                     charFrom++;
       
  1686             if (charFrom < ilen) {
       
  1687                 glyphStart = logClusters[charFrom];
       
  1688                 int charEnd = from + len - 1 - pos;
       
  1689                 if (charEnd >= ilen)
       
  1690                     charEnd = ilen-1;
       
  1691                 int glyphEnd = logClusters[charEnd];
       
  1692                 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
       
  1693                     charEnd++;
       
  1694                 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
       
  1695                 if (glyphStart <= glyphEnd ) {
       
  1696                     QFontEngine *fe = fontEngine(*si);
       
  1697                     glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
       
  1698                     gm.x = qMin(gm.x, m.x + gm.xoff);
       
  1699                     gm.y = qMin(gm.y, m.y + gm.yoff);
       
  1700                     gm.width = qMax(gm.width, m.width+gm.xoff);
       
  1701                     gm.height = qMax(gm.height, m.height+gm.yoff);
       
  1702                     gm.xoff += m.xoff;
       
  1703                     gm.yoff += m.yoff;
       
  1704                 }
       
  1705             }
       
  1706         }
       
  1707     }
       
  1708     return gm;
       
  1709 }
       
  1710 
       
  1711 QFont QTextEngine::font(const QScriptItem &si) const
       
  1712 {
       
  1713     QFont font = fnt;
       
  1714     if (hasFormats()) {
       
  1715         QTextCharFormat f = format(&si);
       
  1716         font = f.font();
       
  1717 
       
  1718         if (block.docHandle() && block.docHandle()->layout()) {
       
  1719             // Make sure we get the right dpi on printers
       
  1720             QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  1721             if (pdev)
       
  1722                 font = QFont(font, pdev);
       
  1723         } else {
       
  1724             font = font.resolve(fnt);
       
  1725         }
       
  1726         QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
       
  1727         if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  1728             if (font.pointSize() != -1)
       
  1729                 font.setPointSize((font.pointSize() * 2) / 3);
       
  1730             else
       
  1731                 font.setPixelSize((font.pixelSize() * 2) / 3);
       
  1732         }
       
  1733     }
       
  1734 
       
  1735     if (si.analysis.flags == QScriptAnalysis::SmallCaps)
       
  1736         font = font.d->smallCapsFont();
       
  1737 
       
  1738     return font;
       
  1739 }
       
  1740 
       
  1741 QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent, QFixed *leading) const
       
  1742 {
       
  1743     QFontEngine *engine = 0;
       
  1744     QFontEngine *scaledEngine = 0;
       
  1745     int script = si.analysis.script;
       
  1746 
       
  1747     QFont font = fnt;
       
  1748     if (hasFormats()) {
       
  1749         QTextCharFormat f = format(&si);
       
  1750         font = f.font();
       
  1751 
       
  1752         if (block.docHandle() && block.docHandle()->layout()) {
       
  1753             // Make sure we get the right dpi on printers
       
  1754             QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  1755             if (pdev)
       
  1756                 font = QFont(font, pdev);
       
  1757         } else {
       
  1758             font = font.resolve(fnt);
       
  1759         }
       
  1760         engine = font.d->engineForScript(script);
       
  1761         QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
       
  1762         if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  1763             if (font.pointSize() != -1)
       
  1764                 font.setPointSize((font.pointSize() * 2) / 3);
       
  1765             else
       
  1766                 font.setPixelSize((font.pixelSize() * 2) / 3);
       
  1767             scaledEngine = font.d->engineForScript(script);
       
  1768         }
       
  1769     } else {
       
  1770         engine = font.d->engineForScript(script);
       
  1771     }
       
  1772 
       
  1773     if (si.analysis.flags == QScriptAnalysis::SmallCaps) {
       
  1774         QFontPrivate *p = font.d->smallCapsFontPrivate();
       
  1775         scaledEngine = p->engineForScript(script);
       
  1776     }
       
  1777 
       
  1778     if (ascent) {
       
  1779         *ascent = engine->ascent();
       
  1780         *descent = engine->descent();
       
  1781         *leading = engine->leading();
       
  1782     }
       
  1783 
       
  1784     if (scaledEngine)
       
  1785         return scaledEngine;
       
  1786     return engine;
       
  1787 }
       
  1788 
       
  1789 struct QJustificationPoint {
       
  1790     int type;
       
  1791     QFixed kashidaWidth;
       
  1792     QGlyphLayout glyph;
       
  1793     QFontEngine *fontEngine;
       
  1794 };
       
  1795 
       
  1796 Q_DECLARE_TYPEINFO(QJustificationPoint, Q_PRIMITIVE_TYPE);
       
  1797 
       
  1798 static void set(QJustificationPoint *point, int type, const QGlyphLayout &glyph, QFontEngine *fe)
       
  1799 {
       
  1800     point->type = type;
       
  1801     point->glyph = glyph;
       
  1802     point->fontEngine = fe;
       
  1803 
       
  1804     if (type >= HB_Arabic_Normal) {
       
  1805         QChar ch(0x640); // Kashida character
       
  1806         QGlyphLayoutArray<8> glyphs;
       
  1807         int nglyphs = 7;
       
  1808         fe->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
       
  1809         if (glyphs.glyphs[0] && glyphs.advances_x[0] != 0) {
       
  1810             point->kashidaWidth = glyphs.advances_x[0];
       
  1811         } else {
       
  1812             point->type = HB_NoJustification;
       
  1813             point->kashidaWidth = 0;
       
  1814         }
       
  1815     }
       
  1816 }
       
  1817 
       
  1818 
       
  1819 void QTextEngine::justify(const QScriptLine &line)
       
  1820 {
       
  1821 //     qDebug("justify: line.gridfitted = %d, line.justified=%d", line.gridfitted, line.justified);
       
  1822     if (line.gridfitted && line.justified)
       
  1823         return;
       
  1824 
       
  1825     if (!line.gridfitted) {
       
  1826         // redo layout in device metrics, then adjust
       
  1827         const_cast<QScriptLine &>(line).gridfitted = true;
       
  1828     }
       
  1829 
       
  1830     if ((option.alignment() & Qt::AlignHorizontal_Mask) != Qt::AlignJustify)
       
  1831         return;
       
  1832 
       
  1833     itemize();
       
  1834 
       
  1835     if (!forceJustification) {
       
  1836         int end = line.from + (int)line.length;
       
  1837         if (end == layoutData->string.length())
       
  1838             return; // no justification at end of paragraph
       
  1839         if (end && layoutData->items[findItem(end-1)].analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
       
  1840             return; // no justification at the end of an explicitely separated line
       
  1841     }
       
  1842 
       
  1843     // justify line
       
  1844     int maxJustify = 0;
       
  1845 
       
  1846     // don't include trailing white spaces when doing justification
       
  1847     int line_length = line.length;
       
  1848     const HB_CharAttributes *a = attributes()+line.from;
       
  1849     while (line_length && a[line_length-1].whiteSpace)
       
  1850         --line_length;
       
  1851     // subtract one char more, as we can't justfy after the last character
       
  1852     --line_length;
       
  1853 
       
  1854     if (!line_length)
       
  1855         return;
       
  1856 
       
  1857     int firstItem = findItem(line.from);
       
  1858     int nItems = findItem(line.from + line_length - 1) - firstItem + 1;
       
  1859 
       
  1860     QVarLengthArray<QJustificationPoint> justificationPoints;
       
  1861     int nPoints = 0;
       
  1862 //     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());
       
  1863     QFixed minKashida = 0x100000;
       
  1864 
       
  1865     // we need to do all shaping before we go into the next loop, as we there
       
  1866     // store pointers to the glyph data that could get reallocated by the shaping
       
  1867     // process.
       
  1868     for (int i = 0; i < nItems; ++i) {
       
  1869         QScriptItem &si = layoutData->items[firstItem + i];
       
  1870         if (!si.num_glyphs)
       
  1871             shape(firstItem + i);
       
  1872     }
       
  1873 
       
  1874     for (int i = 0; i < nItems; ++i) {
       
  1875         QScriptItem &si = layoutData->items[firstItem + i];
       
  1876 
       
  1877         int kashida_type = HB_Arabic_Normal;
       
  1878         int kashida_pos = -1;
       
  1879 
       
  1880         int start = qMax(line.from - si.position, 0);
       
  1881         int end = qMin(line.from + line_length - (int)si.position, length(firstItem+i));
       
  1882 
       
  1883         unsigned short *log_clusters = logClusters(&si);
       
  1884 
       
  1885         int gs = log_clusters[start];
       
  1886         int ge = (end == length(firstItem+i) ? si.num_glyphs : log_clusters[end]);
       
  1887 
       
  1888         const QGlyphLayout g = shapedGlyphs(&si);
       
  1889 
       
  1890         for (int i = gs; i < ge; ++i) {
       
  1891             g.justifications[i].type = QGlyphJustification::JustifyNone;
       
  1892             g.justifications[i].nKashidas = 0;
       
  1893             g.justifications[i].space_18d6 = 0;
       
  1894 
       
  1895             justificationPoints.resize(nPoints+3);
       
  1896             int justification = g.attributes[i].justification;
       
  1897 
       
  1898             switch(justification) {
       
  1899             case HB_NoJustification:
       
  1900                 break;
       
  1901             case HB_Space          :
       
  1902                 // fall through
       
  1903             case HB_Arabic_Space   :
       
  1904                 if (kashida_pos >= 0) {
       
  1905 //                     qDebug("kashida position at %d in word", kashida_pos);
       
  1906                     set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
       
  1907                     minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
       
  1908                     maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
       
  1909                     ++nPoints;
       
  1910                 }
       
  1911                 kashida_pos = -1;
       
  1912                 kashida_type = HB_Arabic_Normal;
       
  1913                 // fall through
       
  1914             case HB_Character      :
       
  1915                 set(&justificationPoints[nPoints++], justification, g.mid(i), fontEngine(si));
       
  1916                 maxJustify = qMax(maxJustify, justification);
       
  1917                 break;
       
  1918             case HB_Arabic_Normal  :
       
  1919             case HB_Arabic_Waw     :
       
  1920             case HB_Arabic_BaRa    :
       
  1921             case HB_Arabic_Alef    :
       
  1922             case HB_Arabic_HaaDal  :
       
  1923             case HB_Arabic_Seen    :
       
  1924             case HB_Arabic_Kashida :
       
  1925                 if (justification >= kashida_type) {
       
  1926                     kashida_pos = i;
       
  1927                     kashida_type = justification;
       
  1928                 }
       
  1929             }
       
  1930         }
       
  1931         if (kashida_pos >= 0) {
       
  1932             set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
       
  1933             minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
       
  1934             maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
       
  1935             ++nPoints;
       
  1936         }
       
  1937     }
       
  1938 
       
  1939     QFixed need = line.width - line.textWidth;
       
  1940     if (need < 0) {
       
  1941         // line overflows already!
       
  1942         const_cast<QScriptLine &>(line).justified = true;
       
  1943         return;
       
  1944     }
       
  1945 
       
  1946 //     qDebug("doing justification: textWidth=%x, requested=%x, maxJustify=%d", line.textWidth.value(), line.width.value(), maxJustify);
       
  1947 //     qDebug("     minKashida=%f, need=%f", minKashida.toReal(), need.toReal());
       
  1948 
       
  1949     // distribute in priority order
       
  1950     if (maxJustify >= HB_Arabic_Normal) {
       
  1951         while (need >= minKashida) {
       
  1952             for (int type = maxJustify; need >= minKashida && type >= HB_Arabic_Normal; --type) {
       
  1953                 for (int i = 0; need >= minKashida && i < nPoints; ++i) {
       
  1954                     if (justificationPoints[i].type == type && justificationPoints[i].kashidaWidth <= need) {
       
  1955                         justificationPoints[i].glyph.justifications->nKashidas++;
       
  1956                         // ############
       
  1957                         justificationPoints[i].glyph.justifications->space_18d6 += justificationPoints[i].kashidaWidth.value();
       
  1958                         need -= justificationPoints[i].kashidaWidth;
       
  1959 //                         qDebug("adding kashida type %d with width %x, neednow %x", type, justificationPoints[i].kashidaWidth, need.value());
       
  1960                     }
       
  1961                 }
       
  1962             }
       
  1963         }
       
  1964     }
       
  1965     Q_ASSERT(need >= 0);
       
  1966     if (!need)
       
  1967         goto end;
       
  1968 
       
  1969     maxJustify = qMin(maxJustify, (int)HB_Space);
       
  1970     for (int type = maxJustify; need != 0 && type > 0; --type) {
       
  1971         int n = 0;
       
  1972         for (int i = 0; i < nPoints; ++i) {
       
  1973             if (justificationPoints[i].type == type)
       
  1974                 ++n;
       
  1975         }
       
  1976 //          qDebug("number of points for justification type %d: %d", type, n);
       
  1977 
       
  1978 
       
  1979         if (!n)
       
  1980             continue;
       
  1981 
       
  1982         for (int i = 0; i < nPoints; ++i) {
       
  1983             if (justificationPoints[i].type == type) {
       
  1984                 QFixed add = need/n;
       
  1985 //                  qDebug("adding %x to glyph %x", add.value(), justificationPoints[i].glyph->glyph);
       
  1986                 justificationPoints[i].glyph.justifications[0].space_18d6 = add.value();
       
  1987                 need -= add;
       
  1988                 --n;
       
  1989             }
       
  1990         }
       
  1991 
       
  1992         Q_ASSERT(!need);
       
  1993     }
       
  1994  end:
       
  1995     const_cast<QScriptLine &>(line).justified = true;
       
  1996 }
       
  1997 
       
  1998 void QScriptLine::setDefaultHeight(QTextEngine *eng)
       
  1999 {
       
  2000     QFont f;
       
  2001     QFontEngine *e;
       
  2002 
       
  2003     if (eng->block.docHandle() && eng->block.docHandle()->layout()) {
       
  2004         f = eng->block.charFormat().font();
       
  2005         // Make sure we get the right dpi on printers
       
  2006         QPaintDevice *pdev = eng->block.docHandle()->layout()->paintDevice();
       
  2007         if (pdev)
       
  2008             f = QFont(f, pdev);
       
  2009         e = f.d->engineForScript(QUnicodeTables::Common);
       
  2010     } else {
       
  2011         e = eng->fnt.d->engineForScript(QUnicodeTables::Common);
       
  2012     }
       
  2013 
       
  2014     QFixed other_ascent = e->ascent();
       
  2015     QFixed other_descent = e->descent();
       
  2016     QFixed other_leading = e->leading();
       
  2017     leading = qMax(leading + ascent, other_leading + other_ascent) - qMax(ascent, other_ascent);
       
  2018     ascent = qMax(ascent, other_ascent);
       
  2019     descent = qMax(descent, other_descent);
       
  2020 }
       
  2021 
       
  2022 QTextEngine::LayoutData::LayoutData()
       
  2023 {
       
  2024     memory = 0;
       
  2025     allocated = 0;
       
  2026     memory_on_stack = false;
       
  2027     used = 0;
       
  2028     hasBidi = false;
       
  2029     inLayout = false;
       
  2030     haveCharAttributes = false;
       
  2031     logClustersPtr = 0;
       
  2032     available_glyphs = 0;
       
  2033 }
       
  2034 
       
  2035 QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
       
  2036     : string(str)
       
  2037 {
       
  2038     allocated = _allocated;
       
  2039 
       
  2040     int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
       
  2041     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
       
  2042     available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::spaceNeededForGlyphLayout(1);
       
  2043 
       
  2044     if (available_glyphs < str.length()) {
       
  2045         // need to allocate on the heap
       
  2046         allocated = 0;
       
  2047 
       
  2048         memory_on_stack = false;
       
  2049         memory = 0;
       
  2050         logClustersPtr = 0;
       
  2051     } else {
       
  2052         memory_on_stack = true;
       
  2053         memory = stack_memory;
       
  2054         logClustersPtr = (unsigned short *)(memory + space_charAttributes);
       
  2055 
       
  2056         void *m = memory + space_charAttributes + space_logClusters;
       
  2057         glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
       
  2058         glyphLayout.clear();
       
  2059         memset(memory, 0, space_charAttributes*sizeof(void *));
       
  2060     }
       
  2061     used = 0;
       
  2062     hasBidi = false;
       
  2063     inLayout = false;
       
  2064     haveCharAttributes = false;
       
  2065 }
       
  2066 
       
  2067 QTextEngine::LayoutData::~LayoutData()
       
  2068 {
       
  2069     if (!memory_on_stack)
       
  2070         free(memory);
       
  2071     memory = 0;
       
  2072 }
       
  2073 
       
  2074 void QTextEngine::LayoutData::reallocate(int totalGlyphs)
       
  2075 {
       
  2076     Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs);
       
  2077     if (memory_on_stack && available_glyphs >= totalGlyphs) {
       
  2078         glyphLayout.grow(glyphLayout.data(), totalGlyphs);
       
  2079         return;
       
  2080     }
       
  2081 
       
  2082     int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
       
  2083     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
       
  2084     int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2;
       
  2085 
       
  2086     int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
       
  2087     Q_ASSERT(newAllocated >= allocated);
       
  2088     void **newMem = memory;
       
  2089     newMem = (void **)::realloc(memory_on_stack ? 0 : memory, newAllocated*sizeof(void *));
       
  2090     Q_CHECK_PTR(newMem);
       
  2091     if (memory_on_stack && newMem)
       
  2092         memcpy(newMem, memory, allocated*sizeof(void *));
       
  2093     memory = newMem;
       
  2094     memory_on_stack = false;
       
  2095 
       
  2096     void **m = memory;
       
  2097     m += space_charAttributes;
       
  2098     logClustersPtr = (unsigned short *) m;
       
  2099     m += space_logClusters;
       
  2100 
       
  2101     const int space_preGlyphLayout = space_charAttributes + space_logClusters;
       
  2102     if (allocated < space_preGlyphLayout)
       
  2103         memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
       
  2104 
       
  2105     glyphLayout.grow(reinterpret_cast<char *>(m), totalGlyphs);
       
  2106 
       
  2107     allocated = newAllocated;
       
  2108 }
       
  2109 
       
  2110 // grow to the new size, copying the existing data to the new layout
       
  2111 void QGlyphLayout::grow(char *address, int totalGlyphs)
       
  2112 {
       
  2113     QGlyphLayout oldLayout(address, numGlyphs);
       
  2114     QGlyphLayout newLayout(address, totalGlyphs);
       
  2115 
       
  2116     if (numGlyphs) {
       
  2117         // move the existing data
       
  2118         memmove(newLayout.attributes, oldLayout.attributes, numGlyphs * sizeof(HB_GlyphAttributes));
       
  2119         memmove(newLayout.justifications, oldLayout.justifications, numGlyphs * sizeof(QGlyphJustification));
       
  2120         memmove(newLayout.advances_y, oldLayout.advances_y, numGlyphs * sizeof(QFixed));
       
  2121         memmove(newLayout.advances_x, oldLayout.advances_x, numGlyphs * sizeof(QFixed));
       
  2122         memmove(newLayout.glyphs, oldLayout.glyphs, numGlyphs * sizeof(HB_Glyph));
       
  2123     }
       
  2124 
       
  2125     // clear the new data
       
  2126     newLayout.clear(numGlyphs);
       
  2127 
       
  2128     *this = newLayout;
       
  2129 }
       
  2130 
       
  2131 void QTextEngine::freeMemory()
       
  2132 {
       
  2133     if (!stackEngine) {
       
  2134         delete layoutData;
       
  2135         layoutData = 0;
       
  2136     } else {
       
  2137         layoutData->used = 0;
       
  2138         layoutData->hasBidi = false;
       
  2139         layoutData->inLayout = false;
       
  2140         layoutData->haveCharAttributes = false;
       
  2141     }
       
  2142     for (int i = 0; i < lines.size(); ++i) {
       
  2143         lines[i].justified = 0;
       
  2144         lines[i].gridfitted = 0;
       
  2145     }
       
  2146 }
       
  2147 
       
  2148 int QTextEngine::formatIndex(const QScriptItem *si) const
       
  2149 {
       
  2150     if (specialData && !specialData->resolvedFormatIndices.isEmpty())
       
  2151         return specialData->resolvedFormatIndices.at(si - &layoutData->items[0]);
       
  2152     QTextDocumentPrivate *p = block.docHandle();
       
  2153     if (!p)
       
  2154         return -1;
       
  2155     int pos = si->position;
       
  2156     if (specialData && si->position >= specialData->preeditPosition) {
       
  2157         if (si->position < specialData->preeditPosition + specialData->preeditText.length())
       
  2158             pos = qMax(specialData->preeditPosition - 1, 0);
       
  2159         else
       
  2160             pos -= specialData->preeditText.length();
       
  2161     }
       
  2162     QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
       
  2163     return it.value()->format;
       
  2164 }
       
  2165 
       
  2166 
       
  2167 QTextCharFormat QTextEngine::format(const QScriptItem *si) const
       
  2168 {
       
  2169     QTextCharFormat format;
       
  2170     const QTextFormatCollection *formats = 0;
       
  2171     if (block.docHandle()) {
       
  2172         formats = this->formats();
       
  2173         format = formats->charFormat(formatIndex(si));
       
  2174     }
       
  2175     if (specialData && specialData->resolvedFormatIndices.isEmpty()) {
       
  2176         int end = si->position + length(si);
       
  2177         for (int i = 0; i < specialData->addFormats.size(); ++i) {
       
  2178             const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
       
  2179             if (r.start <= si->position && r.start + r.length >= end) {
       
  2180                 if (!specialData->addFormatIndices.isEmpty())
       
  2181                     format.merge(formats->format(specialData->addFormatIndices.at(i)));
       
  2182                 else
       
  2183                     format.merge(r.format);
       
  2184             }
       
  2185         }
       
  2186     }
       
  2187     return format;
       
  2188 }
       
  2189 
       
  2190 void QTextEngine::addRequiredBoundaries() const
       
  2191 {
       
  2192     if (specialData) {
       
  2193         for (int i = 0; i < specialData->addFormats.size(); ++i) {
       
  2194             const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
       
  2195             setBoundary(r.start);
       
  2196             setBoundary(r.start + r.length);
       
  2197             //qDebug("adding boundaries %d %d", r.start, r.start+r.length);
       
  2198         }
       
  2199     }
       
  2200 }
       
  2201 
       
  2202 bool QTextEngine::atWordSeparator(int position) const
       
  2203 {
       
  2204     const QChar c = layoutData->string.at(position);
       
  2205     switch (c.toLatin1()) {
       
  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     case '^':
       
  2227     case '*':
       
  2228     case '\'':
       
  2229     case '"':
       
  2230     case '~':
       
  2231     case '|':
       
  2232         return true;
       
  2233     default:
       
  2234         return false;
       
  2235     }
       
  2236 }
       
  2237 
       
  2238 bool QTextEngine::atSpace(int position) const
       
  2239 {
       
  2240     const QChar c = layoutData->string.at(position);
       
  2241 
       
  2242     return c == QLatin1Char(' ')
       
  2243         || c == QChar::Nbsp
       
  2244         || c == QChar::LineSeparator
       
  2245         || c == QLatin1Char('\t')
       
  2246         ;
       
  2247 }
       
  2248 
       
  2249 
       
  2250 void QTextEngine::indexAdditionalFormats()
       
  2251 {
       
  2252     if (!block.docHandle())
       
  2253         return;
       
  2254 
       
  2255     specialData->addFormatIndices.resize(specialData->addFormats.count());
       
  2256     QTextFormatCollection * const formats = this->formats();
       
  2257 
       
  2258     for (int i = 0; i < specialData->addFormats.count(); ++i) {
       
  2259         specialData->addFormatIndices[i] = formats->indexForFormat(specialData->addFormats.at(i).format);
       
  2260         specialData->addFormats[i].format = QTextCharFormat();
       
  2261     }
       
  2262 }
       
  2263 
       
  2264 /* These two helper functions are used to determine whether we need to insert a ZWJ character
       
  2265    between the text that gets truncated and the ellipsis. This is important to get
       
  2266    correctly shaped results for arabic text.
       
  2267 */
       
  2268 static bool nextCharJoins(const QString &string, int pos)
       
  2269 {
       
  2270     while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
       
  2271         ++pos;
       
  2272     if (pos == string.length())
       
  2273         return false;
       
  2274     return string.at(pos).joining() != QChar::OtherJoining;
       
  2275 }
       
  2276 
       
  2277 static bool prevCharJoins(const QString &string, int pos)
       
  2278 {
       
  2279     while (pos > 0 && string.at(pos - 1).category() == QChar::Mark_NonSpacing)
       
  2280         --pos;
       
  2281     if (pos == 0)
       
  2282         return false;
       
  2283     return (string.at(pos - 1).joining() == QChar::Dual || string.at(pos - 1).joining() == QChar::Center);
       
  2284 }
       
  2285 
       
  2286 QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const
       
  2287 {
       
  2288 //    qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
       
  2289 
       
  2290     if (flags & Qt::TextShowMnemonic) {
       
  2291         itemize();
       
  2292         for (int i = 0; i < layoutData->items.size(); ++i) {
       
  2293             QScriptItem &si = layoutData->items[i];
       
  2294             if (!si.num_glyphs)
       
  2295                 shape(i);
       
  2296 
       
  2297             HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
       
  2298             unsigned short *logClusters = this->logClusters(&si);
       
  2299             QGlyphLayout glyphs = shapedGlyphs(&si);
       
  2300 
       
  2301             const int end = si.position + length(&si);
       
  2302             for (int i = si.position; i < end - 1; ++i)
       
  2303                 if (layoutData->string.at(i) == QLatin1Char('&')) {
       
  2304                     const int gp = logClusters[i - si.position];
       
  2305                     glyphs.attributes[gp].dontPrint = true;
       
  2306                     attributes[i + 1].charStop = false;
       
  2307                     attributes[i + 1].whiteSpace = false;
       
  2308                     attributes[i + 1].lineBreakType = HB_NoBreak;
       
  2309                     if (i < end - 1
       
  2310                             && layoutData->string.at(i + 1) == QLatin1Char('&'))
       
  2311                         ++i;
       
  2312                 }
       
  2313         }
       
  2314     }
       
  2315 
       
  2316     validate();
       
  2317 
       
  2318     if (mode == Qt::ElideNone
       
  2319         || this->width(0, layoutData->string.length()) <= width
       
  2320         || layoutData->string.length() <= 1)
       
  2321         return layoutData->string;
       
  2322 
       
  2323     QFixed ellipsisWidth;
       
  2324     QString ellipsisText;
       
  2325     {
       
  2326         QChar ellipsisChar(0x2026);
       
  2327 
       
  2328         QFontEngine *fe = fnt.d->engineForScript(QUnicodeTables::Common);
       
  2329 
       
  2330         QGlyphLayoutArray<1> ellipsisGlyph;
       
  2331         {
       
  2332             QFontEngine *feForEllipsis = (fe->type() == QFontEngine::Multi)
       
  2333                 ? static_cast<QFontEngineMulti *>(fe)->engine(0)
       
  2334                 : fe;
       
  2335 
       
  2336             if (feForEllipsis->type() == QFontEngine::Mac)
       
  2337                 feForEllipsis = fe;
       
  2338 
       
  2339             // the lookup can be really slow when we use XLFD fonts
       
  2340             if (feForEllipsis->type() != QFontEngine::XLFD
       
  2341                 && feForEllipsis->canRender(&ellipsisChar, 1)) {
       
  2342                     int nGlyphs = 1;
       
  2343                     feForEllipsis->stringToCMap(&ellipsisChar, 1, &ellipsisGlyph, &nGlyphs, 0);
       
  2344                 }
       
  2345         }
       
  2346 
       
  2347         if (ellipsisGlyph.glyphs[0]) {
       
  2348             ellipsisWidth = ellipsisGlyph.advances_x[0];
       
  2349             ellipsisText = ellipsisChar;
       
  2350         } else {
       
  2351             QString dotDotDot(QLatin1String("..."));
       
  2352 
       
  2353             QGlyphLayoutArray<3> glyphs;
       
  2354             int nGlyphs = 3;
       
  2355             if (!fe->stringToCMap(dotDotDot.constData(), 3, &glyphs, &nGlyphs, 0))
       
  2356                 // should never happen...
       
  2357                 return layoutData->string;
       
  2358             for (int i = 0; i < nGlyphs; ++i)
       
  2359                 ellipsisWidth += glyphs.advances_x[i];
       
  2360             ellipsisText = dotDotDot;
       
  2361         }
       
  2362     }
       
  2363 
       
  2364     const QFixed availableWidth = width - ellipsisWidth;
       
  2365     if (availableWidth < 0)
       
  2366         return QString();
       
  2367 
       
  2368     const HB_CharAttributes *attributes = this->attributes();
       
  2369 
       
  2370     if (mode == Qt::ElideRight) {
       
  2371         QFixed currentWidth;
       
  2372         int pos = 0;
       
  2373         int nextBreak = 0;
       
  2374 
       
  2375         do {
       
  2376             pos = nextBreak;
       
  2377 
       
  2378             ++nextBreak;
       
  2379             while (nextBreak < layoutData->string.length() && !attributes[nextBreak].charStop)
       
  2380                 ++nextBreak;
       
  2381 
       
  2382             currentWidth += this->width(pos, nextBreak - pos);
       
  2383         } while (nextBreak < layoutData->string.length()
       
  2384                  && currentWidth < availableWidth);
       
  2385 
       
  2386         if (nextCharJoins(layoutData->string, pos))
       
  2387             ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
       
  2388 
       
  2389         return layoutData->string.left(pos) + ellipsisText;
       
  2390     } else if (mode == Qt::ElideLeft) {
       
  2391         QFixed currentWidth;
       
  2392         int pos = layoutData->string.length();
       
  2393         int nextBreak = layoutData->string.length();
       
  2394 
       
  2395         do {
       
  2396             pos = nextBreak;
       
  2397 
       
  2398             --nextBreak;
       
  2399             while (nextBreak > 0 && !attributes[nextBreak].charStop)
       
  2400                 --nextBreak;
       
  2401 
       
  2402             currentWidth += this->width(nextBreak, pos - nextBreak);
       
  2403         } while (nextBreak > 0
       
  2404                  && currentWidth < availableWidth);
       
  2405 
       
  2406         if (prevCharJoins(layoutData->string, pos))
       
  2407             ellipsisText.append(QChar(0x200d) /* ZWJ */);
       
  2408 
       
  2409         return ellipsisText + layoutData->string.mid(pos);
       
  2410     } else if (mode == Qt::ElideMiddle) {
       
  2411         QFixed leftWidth;
       
  2412         QFixed rightWidth;
       
  2413 
       
  2414         int leftPos = 0;
       
  2415         int nextLeftBreak = 0;
       
  2416 
       
  2417         int rightPos = layoutData->string.length();
       
  2418         int nextRightBreak = layoutData->string.length();
       
  2419 
       
  2420         do {
       
  2421             leftPos = nextLeftBreak;
       
  2422             rightPos = nextRightBreak;
       
  2423 
       
  2424             ++nextLeftBreak;
       
  2425             while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].charStop)
       
  2426                 ++nextLeftBreak;
       
  2427 
       
  2428             --nextRightBreak;
       
  2429             while (nextRightBreak > 0 && !attributes[nextRightBreak].charStop)
       
  2430                 --nextRightBreak;
       
  2431 
       
  2432             leftWidth += this->width(leftPos, nextLeftBreak - leftPos);
       
  2433             rightWidth += this->width(nextRightBreak, rightPos - nextRightBreak);
       
  2434         } while (nextLeftBreak < layoutData->string.length()
       
  2435                  && nextRightBreak > 0
       
  2436                  && leftWidth + rightWidth < availableWidth);
       
  2437 
       
  2438         if (nextCharJoins(layoutData->string, leftPos))
       
  2439             ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
       
  2440         if (prevCharJoins(layoutData->string, rightPos))
       
  2441             ellipsisText.append(QChar(0x200d) /* ZWJ */);
       
  2442 
       
  2443         return layoutData->string.left(leftPos) + ellipsisText + layoutData->string.mid(rightPos);
       
  2444     }
       
  2445 
       
  2446     return layoutData->string;
       
  2447 }
       
  2448 
       
  2449 void QTextEngine::setBoundary(int strPos) const
       
  2450 {
       
  2451     if (strPos <= 0 || strPos >= layoutData->string.length())
       
  2452         return;
       
  2453 
       
  2454     int itemToSplit = 0;
       
  2455     while (itemToSplit < layoutData->items.size() && layoutData->items[itemToSplit].position <= strPos)
       
  2456         itemToSplit++;
       
  2457     itemToSplit--;
       
  2458     if (layoutData->items[itemToSplit].position == strPos) {
       
  2459         // already a split at the requested position
       
  2460         return;
       
  2461     }
       
  2462     splitItem(itemToSplit, strPos - layoutData->items[itemToSplit].position);
       
  2463 }
       
  2464 
       
  2465 void QTextEngine::splitItem(int item, int pos) const
       
  2466 {
       
  2467     if (pos <= 0)
       
  2468         return;
       
  2469 
       
  2470     layoutData->items.insert(item + 1, QScriptItem(layoutData->items[item]));
       
  2471     QScriptItem &oldItem = layoutData->items[item];
       
  2472     QScriptItem &newItem = layoutData->items[item+1];
       
  2473     newItem.position += pos;
       
  2474 
       
  2475     if (oldItem.num_glyphs) {
       
  2476         // already shaped, break glyphs aswell
       
  2477         int breakGlyph = logClusters(&oldItem)[pos];
       
  2478 
       
  2479         newItem.num_glyphs = oldItem.num_glyphs - breakGlyph;
       
  2480         oldItem.num_glyphs = breakGlyph;
       
  2481         newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph;
       
  2482 
       
  2483         for (int i = 0; i < newItem.num_glyphs; i++)
       
  2484             logClusters(&newItem)[i] -= breakGlyph;
       
  2485 
       
  2486         QFixed w = 0;
       
  2487         const QGlyphLayout g = shapedGlyphs(&oldItem);
       
  2488         for(int j = 0; j < breakGlyph; ++j)
       
  2489             w += g.advances_x[j];
       
  2490 
       
  2491         newItem.width = oldItem.width - w;
       
  2492         oldItem.width = w;
       
  2493     }
       
  2494 
       
  2495 //     qDebug("split at position %d itempos=%d", pos, item);
       
  2496 }
       
  2497 
       
  2498 extern int qt_defaultDpiY();
       
  2499 
       
  2500 QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
       
  2501 {
       
  2502     const QScriptItem &si = layoutData->items[item];
       
  2503 
       
  2504     QFixed dpiScale = 1;
       
  2505     if (block.docHandle() && block.docHandle()->layout()) {
       
  2506         QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
       
  2507         if (pdev)
       
  2508             dpiScale = QFixed::fromReal(pdev->logicalDpiY() / qreal(qt_defaultDpiY()));
       
  2509     } else {
       
  2510         dpiScale = QFixed::fromReal(fnt.d->dpi / qreal(qt_defaultDpiY()));
       
  2511     }
       
  2512 
       
  2513     QList<QTextOption::Tab> tabArray = option.tabs();
       
  2514     if (!tabArray.isEmpty()) {
       
  2515         if (option.textDirection() == Qt::RightToLeft) { // rebase the tabArray positions.
       
  2516             QList<QTextOption::Tab> newTabs;
       
  2517             QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
       
  2518             while(iter != tabArray.end()) {
       
  2519                 QTextOption::Tab tab = *iter;
       
  2520                 if (tab.type == QTextOption::LeftTab)
       
  2521                     tab.type = QTextOption::RightTab;
       
  2522                 else if (tab.type == QTextOption::RightTab)
       
  2523                     tab.type = QTextOption::LeftTab;
       
  2524                 newTabs << tab;
       
  2525                 ++iter;
       
  2526             }
       
  2527             tabArray = newTabs;
       
  2528         }
       
  2529         for (int i = 0; i < tabArray.size(); ++i) {
       
  2530             QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
       
  2531             if (tab > x) {  // this is the tab we need.
       
  2532                 QTextOption::Tab tabSpec = tabArray[i];
       
  2533                 int tabSectionEnd = layoutData->string.count();
       
  2534                 if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
       
  2535                     // find next tab to calculate the width required.
       
  2536                     tab = QFixed::fromReal(tabSpec.position);
       
  2537                     for (int i=item + 1; i < layoutData->items.count(); i++) {
       
  2538                         const QScriptItem &item = layoutData->items[i];
       
  2539                         if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
       
  2540                             tabSectionEnd = item.position;
       
  2541                             break;
       
  2542                         }
       
  2543                     }
       
  2544                 }
       
  2545                 else if (tabSpec.type == QTextOption::DelimiterTab)
       
  2546                     // find delimitor character to calculate the width required
       
  2547                     tabSectionEnd = qMax(si.position, layoutData->string.indexOf(tabSpec.delimiter, si.position) + 1);
       
  2548 
       
  2549                 if (tabSectionEnd > si.position) {
       
  2550                     QFixed length;
       
  2551                     // Calculate the length of text between this tab and the tabSectionEnd
       
  2552                     for (int i=item; i < layoutData->items.count(); i++) {
       
  2553                         QScriptItem &item = layoutData->items[i];
       
  2554                         if (item.position > tabSectionEnd || item.position <= si.position)
       
  2555                             continue;
       
  2556                         shape(i); // first, lets make sure relevant text is already shaped
       
  2557                         QGlyphLayout glyphs = this->shapedGlyphs(&item);
       
  2558                         const int end = qMin(item.position + item.num_glyphs, tabSectionEnd) - item.position;
       
  2559                         for (int i=0; i < end; i++)
       
  2560                             length += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
       
  2561                         if (end + item.position == tabSectionEnd && tabSpec.type == QTextOption::DelimiterTab) // remove half of matching char
       
  2562                             length -= glyphs.advances_x[end] / 2 * !glyphs.attributes[end].dontPrint;
       
  2563                     }
       
  2564 
       
  2565                     switch (tabSpec.type) {
       
  2566                     case QTextOption::CenterTab:
       
  2567                         length /= 2;
       
  2568                         // fall through
       
  2569                     case QTextOption::DelimiterTab:
       
  2570                         // fall through
       
  2571                     case QTextOption::RightTab:
       
  2572                         tab = QFixed::fromReal(tabSpec.position) * dpiScale - length;
       
  2573                         if (tab < 0) // default to tab taking no space
       
  2574                             return QFixed();
       
  2575                         break;
       
  2576                     case QTextOption::LeftTab:
       
  2577                         break;
       
  2578                     }
       
  2579                 }
       
  2580                 return tab - x;
       
  2581             }
       
  2582         }
       
  2583     }
       
  2584     QFixed tab = QFixed::fromReal(option.tabStop());
       
  2585     if (tab <= 0)
       
  2586         tab = 80; // default
       
  2587     tab *= dpiScale;
       
  2588     QFixed nextTabPos = ((x / tab).truncate() + 1) * tab;
       
  2589     QFixed tabWidth = nextTabPos - x;
       
  2590 
       
  2591     return tabWidth;
       
  2592 }
       
  2593 
       
  2594 void QTextEngine::resolveAdditionalFormats() const
       
  2595 {
       
  2596     if (!specialData || specialData->addFormats.isEmpty()
       
  2597         || !block.docHandle()
       
  2598         || !specialData->resolvedFormatIndices.isEmpty())
       
  2599         return;
       
  2600 
       
  2601     QTextFormatCollection *collection = this->formats();
       
  2602 
       
  2603     specialData->resolvedFormatIndices.clear();
       
  2604     QVector<int> indices(layoutData->items.count());
       
  2605     for (int i = 0; i < layoutData->items.count(); ++i) {
       
  2606         QTextCharFormat f = format(&layoutData->items.at(i));
       
  2607         indices[i] = collection->indexForFormat(f);
       
  2608     }
       
  2609     specialData->resolvedFormatIndices = indices;
       
  2610 }
       
  2611 
       
  2612 QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
       
  2613     : _layoutData(string, _memory, MemSize)
       
  2614 {
       
  2615     fnt = f;
       
  2616     text = string;
       
  2617     stackEngine = true;
       
  2618     layoutData = &_layoutData;
       
  2619 }
       
  2620 
       
  2621 QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format)
       
  2622     : justified(false), underlineStyle(QTextCharFormat::NoUnderline), charFormat(format),
       
  2623       num_chars(0), chars(0), logClusters(0), f(0), fontEngine(0)
       
  2624 {
       
  2625     // explicitly initialize flags so that initFontAttributes can be called
       
  2626     // multiple times on the same TextItem
       
  2627     flags = 0;
       
  2628     if (si.analysis.bidiLevel %2)
       
  2629         flags |= QTextItem::RightToLeft;
       
  2630     ascent = si.ascent;
       
  2631     descent = si.descent;
       
  2632     f = font;
       
  2633     fontEngine = f->d->engineForScript(si.analysis.script);
       
  2634     Q_ASSERT(fontEngine);
       
  2635 
       
  2636     if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
       
  2637         underlineStyle = format.underlineStyle();
       
  2638     } else if (format.boolProperty(QTextFormat::FontUnderline)
       
  2639                || f->d->underline) {
       
  2640         underlineStyle = QTextCharFormat::SingleUnderline;
       
  2641     }
       
  2642 
       
  2643     // compat
       
  2644     if (underlineStyle == QTextCharFormat::SingleUnderline)
       
  2645         flags |= QTextItem::Underline;
       
  2646 
       
  2647     if (f->d->overline || format.fontOverline())
       
  2648         flags |= QTextItem::Overline;
       
  2649     if (f->d->strikeOut || format.fontStrikeOut())
       
  2650         flags |= QTextItem::StrikeOut;
       
  2651 }
       
  2652 
       
  2653 QTextItemInt QTextItemInt::midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const
       
  2654 {
       
  2655     QTextItemInt ti = *this;
       
  2656     const int end = firstGlyphIndex + numGlyphs;
       
  2657     ti.glyphs = glyphs.mid(firstGlyphIndex, numGlyphs);
       
  2658     ti.fontEngine = fontEngine;
       
  2659 
       
  2660     if (logClusters && chars) {
       
  2661         const int logClusterOffset = logClusters[0];
       
  2662         while (logClusters[ti.chars - chars] - logClusterOffset < firstGlyphIndex)
       
  2663             ++ti.chars;
       
  2664 
       
  2665         ti.logClusters += (ti.chars - chars);
       
  2666 
       
  2667         ti.num_chars = 0;
       
  2668         int char_start = ti.chars - chars;
       
  2669         while (char_start + ti.num_chars < num_chars && ti.logClusters[ti.num_chars] - logClusterOffset < end)
       
  2670             ++ti.num_chars;
       
  2671     }
       
  2672     return ti;
       
  2673 }
       
  2674 
       
  2675 
       
  2676 QTransform qt_true_matrix(qreal w, qreal h, QTransform x)
       
  2677 {
       
  2678     QRectF rect = x.mapRect(QRectF(0, 0, w, h));
       
  2679     return x * QTransform::fromTranslate(-rect.x(), -rect.y());
       
  2680 }
       
  2681 
       
  2682 
       
  2683 glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
       
  2684 {
       
  2685     if (matrix.type() < QTransform::TxTranslate)
       
  2686         return *this;
       
  2687 
       
  2688     glyph_metrics_t m = *this;
       
  2689 
       
  2690     qreal w = width.toReal();
       
  2691     qreal h = height.toReal();
       
  2692     QTransform xform = qt_true_matrix(w, h, matrix);
       
  2693 
       
  2694     QRectF rect(0, 0, w, h);
       
  2695     rect = xform.mapRect(rect);
       
  2696     m.width = QFixed::fromReal(rect.width());
       
  2697     m.height = QFixed::fromReal(rect.height());
       
  2698 
       
  2699     QLineF l = xform.map(QLineF(x.toReal(), y.toReal(), xoff.toReal(), yoff.toReal()));
       
  2700 
       
  2701     m.x = QFixed::fromReal(l.x1());
       
  2702     m.y = QFixed::fromReal(l.y1());
       
  2703 
       
  2704     // The offset is relative to the baseline which is why we use dx/dy of the line
       
  2705     m.xoff = QFixed::fromReal(l.dx());
       
  2706     m.yoff = QFixed::fromReal(l.dy());
       
  2707 
       
  2708     return m;
       
  2709 }
       
  2710 
       
  2711 
       
  2712 QT_END_NAMESPACE