lib Library API Documentation

qcomplextext.cpp

00001 /****************************************************************************
00002 ** $Id: qcomplextext.cpp 229217 2003-06-01 23:39:24Z dfaure $
00003 **
00004 ** Implementation of some internal classes
00005 **
00006 ** Created :
00007 **
00008 ** Copyright (C) 2001 Trolltech AS.  All rights reserved.
00009 **
00010 ** This file is part of the kernel module of the Qt GUI Toolkit.
00011 **
00012 ** This file may be distributed under the terms of the Q Public License
00013 ** as defined by Trolltech AS of Norway and appearing in the file
00014 ** LICENSE.QPL included in the packaging of this file.
00015 **
00016 ** This file may be distributed and/or modified under the terms of the
00017 ** GNU General Public License version 2 as published by the Free Software
00018 ** Foundation and appearing in the file LICENSE.GPL included in the
00019 ** packaging of this file.
00020 **
00021 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00022 ** licenses may use this file in accordance with the Qt Commercial License
00023 ** Agreement provided with the Software.
00024 **
00025 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00026 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00027 **
00028 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00029 **   information about Qt Commercial License Agreements.
00030 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00031 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00032 **
00033 ** Contact info@trolltech.com if any conditions of this licensing are
00034 ** not clear to you.
00035 **
00036 **********************************************************************/
00037 
00038 
00039 #ifndef QT_NO_COMPLEXTEXT
00040 #include "korichtext.h"
00041 //#include "qfontdata_p.h"
00042 #include "qfontmetrics.h"
00043 #include "qrect.h"
00044 
00045 #include <stdlib.h>
00046 
00047 // -----------------------------------------------------
00048 
00049 /* a small helper class used internally to resolve Bidi embedding levels.
00050    Each line of text caches the embedding level at the start of the line for faster
00051    relayouting
00052 */
00053 KoBidiContext::KoBidiContext( uchar l, QChar::Direction e, KoBidiContext *p, bool o )
00054     : level(l) , override(o), dir(e)
00055 {
00056     if ( p )
00057     p->ref();
00058     parent = p;
00059     count = 0;
00060 }
00061 
00062 KoBidiContext::~KoBidiContext()
00063 {
00064     if( parent && parent->deref() )
00065     delete parent;
00066 }
00067 
00068 static QChar *shapeBuffer = 0;
00069 static int shapeBufSize = 0;
00070 
00071 /*
00072    Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
00073    arabic).
00074 
00075    Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
00076    transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
00077 
00078    Right join-causing: dual + center
00079    Left join-causing: dual + right + center
00080 
00081    Rules are as follows (for a string already in visual order, as we have it here):
00082 
00083    R1 Transparent characters do not affect joining behaviour.
00084    R2 A right joining character, that has a right join-causing char on the right will get form XRight
00085    (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
00086    Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
00087    R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
00088          the right will get form XMedial
00089    R5  A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
00090      will get form XRight
00091    R6 A dual joining character, that has a  left join causing char on the left, and no right join causing char on the right
00092      will get form XLeft
00093    R7 Otherwise the character will get form XIsolated
00094 
00095    Additionally we have to do the minimal ligature support for lam-alef ligatures:
00096 
00097    L1 Transparent characters do not affect ligature behaviour.
00098    L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
00099    L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
00100 
00101    The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
00102    previous and left with next in the above rules ;-)
00103 */
00104 
00105 /*
00106   Two small helper functions for arabic shaping. They get the next shape causing character on either
00107   side of the char in question. Implements rule R1.
00108 
00109   leftChar() returns true if the char to the left is a left join-causing char
00110   rightChar() returns true if the char to the right is a right join-causing char
00111 */
00112 static inline const QChar *prevChar( const QString &str, int pos )
00113 {
00114     //kdDebug() << "leftChar: pos=" << pos << endl;
00115     pos--;
00116     const QChar *ch = str.unicode() + pos;
00117     while( pos > -1 ) {
00118     if( !ch->isMark() )
00119         return ch;
00120     pos--;
00121     ch--;
00122     }
00123     return &QChar::replacement;
00124 }
00125 
00126 static inline const QChar *nextChar( const QString &str, int pos)
00127 {
00128     pos++;
00129     int len = str.length();
00130     const QChar *ch = str.unicode() + pos;
00131     while( pos < len ) {
00132     //kdDebug() << "rightChar: " << pos << " isLetter=" << ch.isLetter() << ", joining=" << ch.joining() << endl;
00133     if( !ch->isMark() )
00134         return ch;
00135     // assume it's a transparent char, this might not be 100% correct
00136     pos++;
00137     ch++;
00138     }
00139     return &QChar::replacement;
00140 }
00141 
00142 static inline bool prevVisualCharJoins( const QString &str, int pos)
00143 {
00144     return (     prevChar( str, pos )->joining() != QChar::OtherJoining );
00145 }
00146 
00147 static inline bool nextVisualCharJoins( const QString &str, int pos)
00148 {
00149     QChar::Joining join = nextChar( str, pos )->joining();
00150     return ( join == QChar::Dual || join == QChar::Center );
00151 }
00152 
00153 
00154 KoComplexText::Shape KoComplexText::glyphVariant( const QString &str, int pos)
00155 {
00156     // ignores L1 - L3, done in the codec
00157     QChar::Joining joining = str[pos].joining();
00158     //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
00159     switch ( joining ) {
00160     case QChar::OtherJoining:
00161     case QChar::Center:
00162         // these don't change shape
00163         return XIsolated;
00164     case QChar::Right:
00165         // only rule R2 applies
00166         if( nextVisualCharJoins( str, pos ) )
00167         return XFinal;
00168         return XIsolated;
00169     case QChar::Dual:
00170         bool right = nextVisualCharJoins( str, pos );
00171         bool left = prevVisualCharJoins( str, pos );
00172         //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
00173         if( right && left )
00174         return XMedial;
00175         else if ( right )
00176         return XFinal;
00177         else if ( left )
00178         return XInitial;
00179         else
00180         return XIsolated;
00181     }
00182     return XIsolated;
00183 }
00184 
00185 /* and the same thing for logical ordering :)
00186  */
00187 static inline bool prevLogicalCharJoins( const QString &str, int pos)
00188 {
00189     return (     nextChar( str, pos )->joining() != QChar::OtherJoining );
00190 }
00191 
00192 static inline bool nextLogicalCharJoins( const QString &str, int pos)
00193 {
00194     QChar::Joining join = prevChar( str, pos )->joining();
00195     return ( join == QChar::Dual || join == QChar::Center );
00196 }
00197 
00198 
00199 KoComplexText::Shape KoComplexText::glyphVariantLogical( const QString &str, int pos)
00200 {
00201     // ignores L1 - L3, ligatures are job of the codec
00202     QChar::Joining joining = str[pos].joining();
00203     //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
00204     switch ( joining ) {
00205     case QChar::OtherJoining:
00206     case QChar::Center:
00207         // these don't change shape
00208         return XIsolated;
00209     case QChar::Right:
00210         // only rule R2 applies
00211         if( nextLogicalCharJoins( str, pos ) )
00212         return XFinal;
00213         return XIsolated;
00214     case QChar::Dual:
00215         bool right = nextLogicalCharJoins( str, pos );
00216         bool left = prevLogicalCharJoins( str, pos );
00217         //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
00218         if( right && left )
00219         return XMedial;
00220         else if ( right )
00221         return XFinal;
00222         else if ( left )
00223         return XInitial;
00224         else
00225         return XIsolated;
00226     }
00227     return XIsolated;
00228 }
00229 
00230 // -------------------------------------------------------------
00231 
00232 // The unicode to unicode shaping codec.
00233 // does only presentation forms B at the moment, but that should be enough for
00234 // simple display
00235 static const ushort arabicUnicodeMapping[256][2] = {
00236     // base of shaped forms, and number-1 of them ( 0 for non shaping,
00237     // 1 for right binding and 3 for dual binding
00238     { 0x0600, 0 }, // 0x600
00239     { 0x0601, 0 }, // 0x601
00240     { 0x0602, 0 }, // 0x602
00241     { 0x0603, 0 }, // 0x603
00242     { 0x0604, 0 }, // 0x604
00243     { 0x0605, 0 }, // 0x605
00244     { 0x0606, 0 }, // 0x606
00245     { 0x0607, 0 }, // 0x607
00246     { 0x0608, 0 }, // 0x608
00247     { 0x0609, 0 }, // 0x609
00248     { 0x060a, 0 }, // 0x60a
00249     { 0x060b, 0 }, // 0x60b
00250     { 0x060c, 0 }, // 0x60c     Arabic comma
00251     { 0x060d, 0 }, // 0x60d
00252     { 0x060e, 0 }, // 0x60e
00253     { 0x060f, 0 }, // 0x60f
00254 
00255     { 0x0610, 0 }, // 0x610
00256     { 0x0611, 0 }, // 0x611
00257     { 0x0612, 0 }, // 0x612
00258     { 0x0613, 0 }, // 0x613
00259     { 0x0614, 0 }, // 0x614
00260     { 0x0615, 0 }, // 0x615
00261     { 0x0616, 0 }, // 0x616
00262     { 0x0617, 0 }, // 0x617
00263     { 0x0618, 0 }, // 0x618
00264     { 0x0619, 0 }, // 0x619
00265     { 0x061a, 0 }, // 0x61a
00266     { 0x061b, 0 }, // 0x61b     Arabic semicolon
00267     { 0x061c, 0 }, // 0x61c
00268     { 0x061d, 0 }, // 0x61d
00269     { 0x061e, 0 }, // 0x61e
00270     { 0x061f, 0 }, // 0x61f     Arabic question mark
00271 
00272     { 0x0620, 0 }, // 0x620
00273     { 0xfe80, 0 }, // 0x621     Hamza
00274     { 0xfe81, 1 }, // 0x622     R       Alef with Madda above
00275     { 0xfe83, 1 }, // 0x623     R       Alef with Hamza above
00276     { 0xfe85, 1 }, // 0x624     R       Waw with Hamza above
00277     { 0xfe87, 1 }, // 0x625     R       Alef with Hamza below
00278     { 0xfe89, 3 }, // 0x626     D       Yeh with Hamza above
00279     { 0xfe8d, 1 }, // 0x627     R       Alef
00280     { 0xfe8f, 3 }, // 0x628     D       Beh
00281     { 0xfe93, 1 }, // 0x629     R       Teh Marbuta
00282     { 0xfe95, 3 }, // 0x62a     D       Teh
00283     { 0xfe99, 3 }, // 0x62b     D       Theh
00284     { 0xfe9d, 3 }, // 0x62c     D       Jeem
00285     { 0xfea1, 3 }, // 0x62d     D       Hah
00286     { 0xfea5, 3 }, // 0x62e     D       Khah
00287     { 0xfea9, 1 }, // 0x62f     R       Dal
00288 
00289     { 0xfeab, 1 }, // 0x630     R       Thal
00290     { 0xfead, 1 }, // 0x631     R       Reh
00291     { 0xfeaf, 1 }, // 0x632     R       Zain
00292     { 0xfeb1, 1 }, // 0x633     D       Seen
00293     { 0xfeb5, 3 }, // 0x634     D       Sheen
00294     { 0xfeb9, 3 }, // 0x635     D       Sad
00295     { 0xfebd, 3 }, // 0x636     D       Dad
00296     { 0xfec1, 3 }, // 0x637     D       Tah
00297     { 0xfec5, 3 }, // 0x638     D       Zah
00298     { 0xfec9, 3 }, // 0x639     D       Ain
00299     { 0xfecd, 3 }, // 0x63a     D       Ghain
00300     { 0x063b, 0 }, // 0x63b
00301     { 0x063c, 0 }, // 0x63c
00302     { 0x063d, 0 }, // 0x63d
00303     { 0x063e, 0 }, // 0x63e
00304     { 0x063f, 0 }, // 0x63f
00305 
00306     { 0x0640, 0 }, // 0x640     C       Tatweel
00307     { 0xfed1, 3 }, // 0x641     D       Feh
00308     { 0xfed5, 3 }, // 0x642     D       Qaf
00309     { 0xfed9, 3 }, // 0x643     D       Kaf
00310     { 0xfedd, 3 }, // 0x644     D       Lam
00311     { 0xfee1, 3 }, // 0x645     D       Meem
00312     { 0xfee5, 3 }, // 0x646     D       Noon
00313     { 0xfee9, 3 }, // 0x647     D       Heh
00314     { 0xfeed, 1 }, // 0x648     R       Waw
00315     { 0xfeef, 1 }, // 0x649     R       Alef Maksura // ### Dual according to newest arabicshaping.txt
00316     { 0xfef1, 3 }, // 0x64a     D       Yeh
00317     { 0x064b, 0 }, // 0x64b     Mark Fathatan
00318     { 0x064c, 0 }, // 0x64c     Mark Dammatan
00319     { 0x064d, 0 }, // 0x64d     Mark Kasratan
00320     { 0x064e, 0 }, // 0x64e     Mark Fatha
00321     { 0x064f, 0 }, // 0x64f     Mark Damma
00322 
00323     { 0x0650, 0 }, // 0x650     Mark Kasra
00324     { 0x0651, 0 }, // 0x651     Mark Shadda
00325     { 0x0652, 0 }, // 0x652     Mark Sukan
00326     // these do not exist in latin6 anymore:
00327     { 0x0653, 0 }, // 0x653     Mark Maddah above
00328     { 0x0654, 0 }, // 0x654     Mark Hamza above
00329     { 0x0655, 0 }, // 0x655     Mark Hamza below
00330     { 0x0656, 0 }, // 0x656
00331     { 0x0657, 0 }, // 0x657
00332     { 0x0658, 0 }, // 0x658
00333     { 0x0659, 0 }, // 0x659
00334     { 0x065a, 0 }, // 0x65a
00335     { 0x065b, 0 }, // 0x65b
00336     { 0x065c, 0 }, // 0x65c
00337     { 0x065d, 0 }, // 0x65d
00338     { 0x065e, 0 }, // 0x65e
00339     { 0x065f, 0 }, // 0x65f
00340 
00341     { 0x0660, 0 }, // 0x660     Arabic 0
00342     { 0x0661, 0 }, // 0x661     Arabic 1
00343     { 0x0662, 0 }, // 0x662     Arabic 2
00344     { 0x0663, 0 }, // 0x663     Arabic 3
00345     { 0x0664, 0 }, // 0x664     Arabic 4
00346     { 0x0665, 0 }, // 0x665     Arabic 5
00347     { 0x0666, 0 }, // 0x666     Arabic 6
00348     { 0x0667, 0 }, // 0x667     Arabic 7
00349     { 0x0668, 0 }, // 0x668     Arabic 8
00350     { 0x0669, 0 }, // 0x669     Arabic 9
00351     { 0x066a, 0 }, // 0x66a     Arabic % sign
00352     { 0x066b, 0 }, // 0x66b     Arabic decimal separator
00353     { 0x066c, 0 }, // 0x66c     Arabic thousands separator
00354     { 0x066d, 0 }, // 0x66d     Arabic five pointed star
00355     { 0x066e, 0 }, // 0x66e
00356     { 0x066f, 0 }, // 0x66f
00357 
00358     // ### some glyphs do not have shaped mappings in the presentation forms A.
00359     // these have the shaping set to 0 for the moment. Will have to find out better mappings for them.
00360     { 0x0670, 0 }, // 0x670
00361     { 0xfb50, 1 }, // 0x671 R   Alef Wasla
00362     { 0x0672, 0 }, // 0x672 R    Alef with wavy Hamza above
00363     { 0x0673, 0 }, // 0x673 R   Alef with wavy Hamza below
00364     { 0x0674, 0 }, // 0x674 U   High Hamza
00365     { 0x0675, 0 }, // 0x675 R   High Hamza Alef
00366     { 0x0676, 0 }, // 0x676 R   High Hamza Wav
00367     { 0xfbdd, 0 }, // 0x677 R   U with hamza above // ### only isolated form found...
00368     { 0x0678, 0 }, // 0x678 D   High hamza yeh
00369     { 0xfb66, 3 }, // 0x679 D   ttheh
00370     { 0xfb5e, 3 }, // 0x67a D   theheh
00371     { 0xfb52, 3 }, // 0x67b D   beeh
00372     { 0x067c, 0 }, // 0x67cD    teh with ring
00373     { 0x067d, 0 }, // 0x67d D   teh with three dots above downwards
00374     { 0xfb56, 3 }, // 0x67e D   peh
00375     { 0xfb62, 3 }, // 0x67f D   teheh
00376 
00377     { 0xfb5a, 3 }, // 0x680 D   beheh
00378     { 0x0681, 0 }, // 0x681 D   hah with hamza above
00379     { 0x0682, 0 }, // 0x682 D   hah with two dots vertical above
00380     { 0xfb76, 3 }, // 0x683 D   nyeh
00381     { 0xfb72, 3 }, // 0x684 D   dyeh
00382     { 0x0685, 0 }, // 0x685 D   hah with three dots above
00383     { 0xfb7a, 3 }, // 0x686 D   tcheh
00384     { 0xfb7e, 3 }, // 0x687 D   tcheheh
00385     { 0xfb88, 1 }, // 0x688 R   ddal
00386     { 0x0689, 0 }, // 0x689 R   dal with ring
00387     { 0x068a, 0 }, // 0x68a R   dal with dot
00388     { 0x068b, 0 }, // 0x68b R   dal with dot below and small tah
00389     { 0xfb84, 1 }, // 0x68cR    dahal
00390     { 0xfb82, 1 }, // 0x68d R   ddahal
00391     { 0xfb86, 1 }, // 0x68e R   dul
00392     { 0x068f, 0 }, // 0x68f R   dal with three dots above downwards
00393 
00394     { 0x0690, 0 }, // 0x690 R   dal with four dots above
00395     { 0xfb8c, 1 }, // 0x691 R   rreh
00396     { 0x0692, 0 }, // 0x692 R   reh with small v
00397     { 0x0693, 0 }, // 0x693 R   reh with ring
00398     { 0x0694, 0 }, // 0x694 R   reh with dot below
00399     { 0x0695, 0 }, // 0x695 R   reh with small v below
00400     { 0x0696, 0 }, // 0x696 R   reh with dot below and dot above
00401     { 0x0697, 0 }, // 0x697 R   reh with two dots above
00402     { 0xfb8a, 1 }, // 0x698 R   jeh
00403     { 0x0699, 0 }, // 0x699 R   reh with four dots above
00404     { 0x069a, 0 }, // 0x69a D   seen with dot below and dot above
00405     { 0x069b, 0 }, // 0x69b D   seen with three dots below
00406     { 0x069c, 0 }, // 0x69cD    seen with three dots below and three dots above
00407     { 0x069d, 0 }, // 0x69d D   sad with two dots below
00408     { 0x069e, 0 }, // 0x69e D   sad with three dots above
00409     { 0x069f, 0 }, // 0x69f D   tah with three dots above
00410 
00411     { 0x06a0, 0 }, // 0x6a0 D   ain with three dots above
00412     { 0x06a1, 0 }, // 0x6a1 D   dotless feh
00413     { 0x06a2, 0 }, // 0x6a2 D   feh with dot moved below
00414     { 0x06a3, 0 }, // 0x6a3 D   feh with dot below
00415     { 0xfb6a, 3 }, // 0x6a4 D   veh
00416     { 0x06a5, 0 }, // 0x6a5 D   feh with three dots below
00417     { 0xfb6e, 3 }, // 0x6a6 D   peheh
00418     { 0x06a7, 0 }, // 0x6a7 D   qaf with dot above
00419     { 0x06a8, 0 }, // 0x6a8 D   qaf woith three dots above
00420     { 0xfb8e, 3 }, // 0x6a9 D   keheh
00421     { 0x06aa, 0 }, // 0x6aa D   swash kaf
00422     { 0x06ab, 0 }, // 0x6ab D   kaf with ring
00423     { 0x06ac, 0 }, // 0x6acD    kaf with dot above
00424     { 0xfbd3, 3 }, // 0x6ad D   ng
00425     { 0x06ae, 0 }, // 0x6ae D   kaf with three dots below
00426     { 0xfb92, 3 }, // 0x6af D   gaf
00427 
00428     { 0x06b0, 0 }, // 0x6b0 D   gaf with ring
00429     { 0xfb9a, 3 }, // 0x6b1 D   ngoeh
00430     { 0x06b2, 0 }, // 0x6b2 D   gaf with two dots below
00431     { 0xfb96, 3 }, // 0x6b3 D   gueh
00432     { 0x06b4, 0 }, // 0x6b4 D   gaf with three dots above
00433     { 0x06b5, 0 }, // 0x6b5 D   lam with small v
00434     { 0x06b6, 0 }, // 0x6b6 D   lam with dot above
00435     { 0x06b7, 0 }, // 0x6b7 D   lam with three dots above
00436     { 0x06b8, 0 }, // 0x6b8 D   lam with three dots below
00437     { 0x06b9, 0 }, // 0x6b9 D   noon with dot below
00438     { 0xfb9e, 1 }, // 0x6ba R   noon ghunna
00439     { 0xfba0, 3 }, // 0x6bb D   rnoon
00440     { 0x06bc, 0 }, // 0x6bcD    noon with ring
00441     { 0x06bd, 0 }, // 0x6bd D   noon with three dots above
00442     { 0xfbaa, 3 }, // 0x6be D   heh doachashmee
00443     { 0x06bf, 0 }, // 0x6bf D   tcheh with dot above
00444 
00445     { 0xfba4, 1 }, // 0x6c0 R   heh with yeh above = ligature hamza on hah (06d5 + 0654)
00446     { 0xfba6, 3 }, // 0x6c1 D   heh goal
00447     { 0x06c2, 0 }, // 0x6c2 R   heh goal with hamza above (06c1 + 0654)
00448     { 0x06c3, 0 }, // 0x6c3 R   teh marbuta goal
00449     { 0x06c4, 0 }, // 0x6c4 R   waw with ring
00450     { 0xfbe0, 1 }, // 0x6c5 R   kirghiz oe
00451     { 0xfbd9, 1 }, // 0x6c6 R   oe
00452     { 0xfbd7, 1 }, // 0x6c7 R   u
00453     { 0xfbdb, 1 }, // 0x6c8 R   yu
00454     { 0xfbe2, 1 }, // 0x6c9 R   kirghiz yu
00455     { 0x06ca, 0 }, // 0x6ca R   waw with teo dots above
00456     { 0xfbde, 1 }, // 0x6cb R   ve
00457     { 0x06cc, 0 }, // 0x6cc D   farsi yeh
00458     { 0x06cd, 0 }, // 0x6cd R   yeh with tail
00459     { 0x06ce, 0 }, // 0x6ce D   yeh with small v
00460     { 0x06cf, 0 }, // 0x6cf R   waw with dot above
00461 
00462     { 0xfbe4, 3 }, // 0x6d0 D   e
00463     { 0x06d1, 0 }, // 0x6d1 D   yeh with three dots below
00464     { 0xfbae, 1 }, // 0x6d2 R   yeh barree
00465     { 0xfbb0, 1 }, // 0x6d3 R   yeh barree with hamza above
00466     { 0x06d4, 0 }, // 0x6d4 U   full stop
00467     { 0x06d5, 0 }, // 0x6d5 D   ae
00468     { 0x06d6, 0 }, // 0x6d6     koreanic annotaion signs
00469     { 0x06d7, 0 }, // 0x6d7     ...
00470     { 0x06d8, 0 }, // 0x6d8
00471     { 0x06d9, 0 }, // 0x6d9
00472     { 0x06da, 0 }, // 0x6da
00473     { 0x06db, 0 }, // 0x6db
00474     { 0x06dc, 0 }, // 0x6dc
00475     { 0x06dd, 0 }, // 0x6dd
00476     { 0x06de, 0 }, // 0x6de
00477     { 0x06df, 0 }, // 0x6df
00478 
00479     { 0x06e0, 0 }, // 0x6e0
00480     { 0x06e1, 0 }, // 0x6e1
00481     { 0x06e2, 0 }, // 0x6e2
00482     { 0x06e3, 0 }, // 0x6e3
00483     { 0x06e4, 0 }, // 0x6e4
00484     { 0x06e5, 0 }, // 0x6e5
00485     { 0x06e6, 0 }, // 0x6e6
00486     { 0x06e7, 0 }, // 0x6e7
00487     { 0x06e8, 0 }, // 0x6e8
00488     { 0x06e9, 0 }, // 0x6e9
00489     { 0x06ea, 0 }, // 0x6ea
00490     { 0x06eb, 0 }, // 0x6eb
00491     { 0x06ec, 0 }, // 0x6ec
00492     { 0x06ed, 0 }, // 0x6ed
00493     { 0x06ee, 0 }, // 0x6ee
00494     { 0x06ef, 0 }, // 0x6ef
00495 
00496     { 0x06f0, 0 }, // 0x6f0     Arabic indic digit 0
00497     { 0x06f1, 0 }, // 0x6f1
00498     { 0x06f2, 0 }, // 0x6f2
00499     { 0x06f3, 0 }, // 0x6f3
00500     { 0x06f4, 0 }, // 0x6f4
00501     { 0x06f5, 0 }, // 0x6f5
00502     { 0x06f6, 0 }, // 0x6f6
00503     { 0x06f7, 0 }, // 0x6f7
00504     { 0x06f8, 0 }, // 0x6f8
00505     { 0x06f9, 0 }, // 0x6f9     Arabic indic digit 9
00506     { 0x06fa, 0 }, // 0x6fa D   Sheen with dot below
00507     { 0x06fb, 0 }, // 0x6fb D   dad with dot below
00508     { 0x06fc, 0 }, // 0x6fc D   ghain with dot below
00509     { 0x06fd, 0 }, // 0x6fd     Sindhi ampersand
00510     { 0x06fe, 0 }, // 0x6fe     sindhi postposition
00511     { 0x06ff, 0 }, // 0x6ff
00512 
00513 };
00514 
00515 // this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
00516 // of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
00517 // medial to the final form
00518 static const ushort arabicUnicodeLamAlefMapping[6][4] = {
00519     { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622        R       Alef with Madda above
00520     { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623        R       Alef with Hamza above
00521     { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624        R       Waw with Hamza above
00522     { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625        R       Alef with Hamza below
00523     { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626        D       Yeh with Hamza above
00524     { 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627         R       Alef
00525 };
00526 
00527 static inline int getShape( const QChar * /* base */, uchar cell, int shape,
00528                 const QFontMetrics * /* fm */ )
00529 {
00530     uint ch = arabicUnicodeMapping[cell][0] + shape;
00531     /*
00532     // we revert to the unshaped glyph in case the shaped version doesn't exist
00533     if ( fm && !fm->inFont( ch ) ) {
00534     switch( shape ) {
00535     case KoComplexText::XIsolated:
00536     break; // try base form
00537     case KoComplexText::XFinal:
00538     ch -= 1; // try isolated form
00539     break;
00540     case KoComplexText::XInitial:
00541     ch += 1; // try medial form
00542     break;
00543     case KoComplexText::XMedial:
00544     ch -= 1; // try initial form
00545     break;
00546     }
00547     if ( !fm->inFont( ch ) )
00548     ch = *base;
00549     }
00550     */
00551     return ch;
00552 }
00553 
00554 QString KoComplexText::shapedString(const QString& uc, int from, int len, QPainter::TextDirection dir, const QFontMetrics *fm )
00555 {
00556     if( len < 0 )
00557     len = uc.length() - from;
00558     if( len == 0 ) {
00559     return QString::null;
00560     }
00561 
00562     // we have to ignore NSMs at the beginning and add at the end.
00563     int num = uc.length() - from - len;
00564     const QChar *ch = uc.unicode() + from + len;
00565     while ( num > 0 && ch->combiningClass() != 0 ) {
00566     ch++;
00567     num--;
00568     len++;
00569     }
00570     ch = uc.unicode() + from;
00571     while ( len > 0 && ch->combiningClass() != 0 ) {
00572     ch++;
00573     len--;
00574     from++;
00575     }
00576     if ( len == 0 ) return QString::null;
00577 
00578     if( !shapeBuffer || len > shapeBufSize ) {
00579       if( shapeBuffer ) free( (void *) shapeBuffer );
00580       shapeBuffer = (QChar *) malloc( len*sizeof( QChar ) );
00581 //        delete [] shapeBuffer;
00582 //        shapeBuffer = new QChar[ len + 1];
00583     shapeBufSize = len;
00584     }
00585 
00586     int lenOut = 0;
00587     QChar *data = shapeBuffer;
00588     if ( dir == QPainter::RTL )
00589     ch += len - 1;
00590     for ( int i = 0; i < len; i++ ) {
00591     uchar r = ch->row();
00592     uchar c = ch->cell();
00593     if ( r != 0x06 ) {
00594         if ( dir == QPainter::RTL && ch->mirrored() )
00595         *data = ch->mirroredChar();
00596         else
00597         *data = *ch;
00598         data++;
00599         lenOut++;
00600     } else {
00601         int pos = i + from;
00602         if ( dir == QPainter::RTL )
00603         pos = from + len - 1 - i;
00604         int shape = glyphVariantLogical( uc, pos );
00605         //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
00606         // take care of lam-alef ligatures (lam right of alef)
00607         ushort map;
00608         switch ( c ) {
00609         case 0x44: { // lam
00610             const QChar *pch = nextChar( uc, pos );
00611             if ( pch->row() == 0x06 ) {
00612             switch ( pch->cell() ) {
00613                 case 0x22:
00614                 case 0x23:
00615                 case 0x25:
00616                 case 0x27:
00617                 //kdDebug() << " lam of lam-alef ligature" << endl;
00618                 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
00619                 goto next;
00620                 default:
00621                 break;
00622             }
00623             }
00624             break;
00625         }
00626         case 0x22: // alef with madda
00627         case 0x23: // alef with hamza above
00628         case 0x25: // alef with hamza below
00629         case 0x27: // alef
00630             if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
00631             // have a lam alef ligature
00632             //kdDebug() << " alef of lam-alef ligature" << endl;
00633             goto skip;
00634             }
00635         default:
00636             break;
00637         }
00638         map = getShape( ch, c, shape, fm );
00639     next:
00640         *data = map;
00641         data++;
00642         lenOut++;
00643     }
00644     skip:
00645     if ( dir == QPainter::RTL )
00646         ch--;
00647     else
00648         ch++;
00649     }
00650 
00651     if ( dir == QPainter::Auto && !uc.simpleText() ) {
00652     return bidiReorderString( QConstString( shapeBuffer, lenOut ).string() );
00653     }
00654     if ( dir == QPainter::RTL ) {
00655     // reverses the non spacing marks to be again after the base char
00656     QChar *s = shapeBuffer;
00657     int i = 0;
00658     while ( i < lenOut ) {
00659         if ( s->combiningClass() != 0 ) {
00660         // non spacing marks
00661         int clen = 1;
00662         QChar *ch = s;
00663         do {
00664             ch++;
00665             clen++;
00666         } while ( ch->combiningClass() != 0 );
00667 
00668         int j = 0;
00669         QChar *cp = s;
00670         while ( j < clen/2 ) {
00671             QChar tmp = *cp;
00672             *cp = *ch;
00673             *ch = tmp;
00674             cp++;
00675             ch--;
00676             j++;
00677         }
00678         s += clen;
00679         i += clen;
00680         } else {
00681         s++;
00682         i++;
00683         }
00684     }
00685     }
00686 
00687     return QConstString( shapeBuffer, lenOut ).string();
00688 }
00689 
00690 QChar KoComplexText::shapedCharacter( const QString &str, int pos, const QFontMetrics *fm )
00691 {
00692     const QChar *ch = str.unicode() + pos;
00693     if ( ch->row() != 0x06 )
00694     return *ch;
00695     else {
00696     int shape = glyphVariantLogical( str, pos );
00697     //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
00698     // lam aleph ligatures
00699     switch ( ch->cell() ) {
00700         case 0x44: { // lam
00701         const QChar *nch = nextChar( str, pos );
00702         if ( nch->row() == 0x06 ) {
00703             switch ( nch->cell() ) {
00704             case 0x22:
00705             case 0x23:
00706             case 0x25:
00707             case 0x27:
00708                 return QChar(arabicUnicodeLamAlefMapping[nch->cell() - 0x22][shape]);
00709             default:
00710                 break;
00711             }
00712         }
00713         break;
00714         }
00715         case 0x22: // alef with madda
00716         case 0x23: // alef with hamza above
00717         case 0x25: // alef with hamza below
00718         case 0x27: // alef
00719         if ( prevChar( str, pos )->unicode() == 0x0644 )
00720             // have a lam alef ligature
00721             return QChar(0);
00722         default:
00723         break;
00724     }
00725     return QChar( getShape( ch, ch->cell(), shape, fm ) );
00726     }
00727 }
00728 
00729 // Avoid using QFontPrivate, to which we don't have access. We don't use positionMarks() anyway
00730 #if 0
00731 QPointArray KoComplexText::positionMarks( QFontPrivate *f, const QString &str,
00732                      int pos, QRect *boundingRect )
00733 {
00734     int len = str.length();
00735     int nmarks = 0;
00736     while ( pos + nmarks < len && str[pos+nmarks +1].combiningClass() > 0 )
00737     nmarks++;
00738 
00739     if ( !nmarks )
00740     return QPointArray();
00741 
00742     QChar baseChar = KoComplexText::shapedCharacter( str, pos );
00743     QRect baseRect = f->boundingRect( baseChar );
00744     int baseOffset = f->textWidth( str, pos, 1 );
00745 
00746     //kdDebug() << "base char: bounding rect at " << baseRect.x() << "/" << baseRect.y() << " (" << baseRect.width() << "/" << baseRect.height() << ")" << endl;
00747     int offset = f->actual.pixelSize / 10 + 1;
00748     //kdDebug() << "offset = " << offset << endl;
00749     QPointArray pa( nmarks );
00750     int i;
00751     unsigned char lastCmb = 0;
00752     QRect attachmentRect;
00753     if ( boundingRect )
00754     *boundingRect = baseRect;
00755     for( i = 0; i < nmarks; i++ ) {
00756     QChar mark = str[pos+i+1];
00757     unsigned char cmb = mark.combiningClass();
00758     if ( cmb < 200 ) {
00759         // fixed position classes. We approximate by mapping to one of the others.
00760         // currently I added only the ones for arabic, hebrew and thai.
00761 
00762         // ### add a bit more offset to arabic, a bit hacky
00763         if ( cmb >= 27 && cmb <= 36 )
00764         offset +=1;
00765         // below
00766         if ( (cmb >= 10 && cmb <= 18) ||
00767          cmb == 20 || cmb == 22 ||
00768          cmb == 29 || cmb == 32 )
00769         cmb = QChar::Combining_Below;
00770         // above
00771         else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
00772               cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
00773         cmb = QChar::Combining_Above;
00774         //below-right
00775         else if ( cmb == 103 )
00776         cmb = QChar::Combining_BelowRight;
00777         // above-right
00778         else if ( cmb == 24 || cmb == 107 )
00779         cmb = QChar::Combining_AboveRight;
00780         else if ( cmb == 25 )
00781         cmb = QChar::Combining_AboveLeft;
00782         // fixed:
00783         //  19 21
00784 
00785     }
00786 
00787     // combining marks of different class don't interact. Reset the rectangle.
00788     if ( cmb != lastCmb ) {
00789         //kdDebug() << "resetting rect" << endl;
00790         attachmentRect = baseRect;
00791     }
00792 
00793     QPoint p;
00794     QRect markRect = f->boundingRect( mark );
00795     switch( cmb ) {
00796     case QChar::Combining_DoubleBelow:
00797         // ### wrong in rtl context!
00798     case QChar::Combining_BelowLeft:
00799         p += QPoint( 0, offset );
00800     case QChar::Combining_BelowLeftAttached:
00801         p += attachmentRect.bottomLeft() - markRect.topLeft();
00802         break;
00803     case QChar::Combining_Below:
00804         p += QPoint( 0, offset );
00805     case QChar::Combining_BelowAttached:
00806         p += attachmentRect.bottomLeft() - markRect.topLeft();
00807         p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
00808         break;
00809         case QChar::Combining_BelowRight:
00810         p += QPoint( 0, offset );
00811     case QChar::Combining_BelowRightAttached:
00812         p += attachmentRect.bottomRight() - markRect.topRight();
00813         break;
00814         case QChar::Combining_Left:
00815         p += QPoint( -offset, 0 );
00816     case QChar::Combining_LeftAttached:
00817         break;
00818         case QChar::Combining_Right:
00819         p += QPoint( offset, 0 );
00820     case QChar::Combining_RightAttached:
00821         break;
00822     case QChar::Combining_DoubleAbove:
00823         // ### wrong in RTL context!
00824     case QChar::Combining_AboveLeft:
00825         p += QPoint( 0, -offset );
00826     case QChar::Combining_AboveLeftAttached:
00827         p += attachmentRect.topLeft() - markRect.bottomLeft();
00828         break;
00829         case QChar::Combining_Above:
00830         p += QPoint( 0, -offset );
00831     case QChar::Combining_AboveAttached:
00832         p += attachmentRect.topLeft() - markRect.bottomLeft();
00833         p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
00834         break;
00835         case QChar::Combining_AboveRight:
00836         p += QPoint( 0, -offset );
00837     case QChar::Combining_AboveRightAttached:
00838         p += attachmentRect.topRight() - markRect.bottomRight();
00839         break;
00840 
00841     case QChar::Combining_IotaSubscript:
00842         default:
00843         break;
00844     }
00845     //kdDebug() << "char=" << mark.unicode() << " combiningClass = " << cmb << " offset=" << p.x() << "/" << p.y() << endl;
00846     markRect.moveBy( p.x(), p.y() );
00847     p += QPoint( -baseOffset, 0 );
00848     attachmentRect |= markRect;
00849     if ( boundingRect )
00850         *boundingRect |= markRect;
00851     lastCmb = cmb;
00852     pa.setPoint( i, p );
00853     }
00854     return pa;
00855 }
00856 #endif
00857 
00858 #define BIDI_DEBUG 0 // 2
00859 #if (BIDI_DEBUG >= 1)
00860 #include <iostream>
00861 #endif
00862 
00863 static QChar::Direction basicDirection(const QString &str, int start = 0)
00864 {
00865     int len = str.length();
00866     int pos = start > len ? len -1 : start;
00867     const QChar *uc = str.unicode() + pos;
00868     while( pos < len ) {
00869     switch( uc->direction() )
00870     {
00871     case QChar::DirL:
00872     case QChar::DirLRO:
00873     case QChar::DirLRE:
00874         return QChar::DirL;
00875     case QChar::DirR:
00876     case QChar::DirAL:
00877     case QChar::DirRLO:
00878     case QChar::DirRLE:
00879         return QChar::DirR;
00880     default:
00881         break;
00882     }
00883     ++pos;
00884     ++uc;
00885     }
00886     if ( start != 0 )
00887     return basicDirection( str );
00888     return QChar::DirL;
00889 }
00890 
00891 // transforms one line of the paragraph to visual order
00892 // the caller is responisble to delete the returned list of KoTextRuns.
00893 QPtrList<KoTextRun> *KoComplexText::bidiReorderLine( KoBidiControl *control, const QString &text, int start, int len,
00894                            QChar::Direction basicDir )
00895 {
00896     int last = start + len - 1;
00897     //printf("doing BiDi reordering from %d to %d!\n", start, last);
00898 
00899     QPtrList<KoTextRun> *runs = new QPtrList<KoTextRun>;
00900     runs->setAutoDelete(TRUE);
00901 
00902     KoBidiContext *context = control->context;
00903     if ( !context ) {
00904     // first line
00905     //if( start != 0 )
00906     //    kdDebug() << "bidiReorderLine::internal error" << endl;
00907     if( basicDir == QChar::DirR || (basicDir == QChar::DirON && text.isRightToLeft() ) ) {
00908         context = new KoBidiContext( 1, QChar::DirR );
00909         control->status.last = QChar::DirR;
00910     } else {
00911         context = new KoBidiContext( 0, QChar::DirL );
00912         control->status.last = QChar::DirL;
00913     }
00914     }
00915 
00916     KoBidiStatus status = control->status;
00917     QChar::Direction dir = QChar::DirON;
00918 
00919     int sor = start;
00920     int eor = start;
00921 
00922     int current = start;
00923     while(current <= last) {
00924     QChar::Direction dirCurrent;
00925     if(current == (int)text.length()) {
00926         KoBidiContext *c = context;
00927         while ( c->parent )
00928         c = c->parent;
00929         dirCurrent = c->dir;
00930     } else if ( current == last ) {
00931         dirCurrent = ( basicDir != QChar::DirON ? basicDir : basicDirection( text, current ) );
00932     } else
00933         dirCurrent = text.at(current).direction();
00934 
00935 
00936 #if (BIDI_DEBUG >= 2)
00937     cout << "directions: dir=" << dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << " level =" << (int)context->level << endl;
00938 #endif
00939 
00940     switch(dirCurrent) {
00941 
00942         // embedding and overrides (X1-X9 in the BiDi specs)
00943     case QChar::DirRLE:
00944         {
00945         uchar level = context->level;
00946         if(level%2) // we have an odd level
00947             level += 2;
00948         else
00949             level++;
00950         if(level < 61) {
00951             runs->append( new KoTextRun(sor, eor, context, dir) );
00952             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
00953             context = new KoBidiContext(level, QChar::DirR, context);
00954             status.last = QChar::DirR;
00955             status.lastStrong = QChar::DirR;
00956         }
00957         break;
00958         }
00959     case QChar::DirLRE:
00960         {
00961         uchar level = context->level;
00962         if(level%2) // we have an odd level
00963             level++;
00964         else
00965             level += 2;
00966         if(level < 61) {
00967             runs->append( new KoTextRun(sor, eor, context, dir) );
00968             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
00969             context = new KoBidiContext(level, QChar::DirL, context);
00970             status.last = QChar::DirL;
00971             status.lastStrong = QChar::DirL;
00972         }
00973         break;
00974         }
00975     case QChar::DirRLO:
00976         {
00977         uchar level = context->level;
00978         if(level%2) // we have an odd level
00979             level += 2;
00980         else
00981             level++;
00982         if(level < 61) {
00983             runs->append( new KoTextRun(sor, eor, context, dir) );
00984             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
00985             context = new KoBidiContext(level, QChar::DirR, context, TRUE);
00986             dir = QChar::DirR;
00987             status.last = QChar::DirR;
00988             status.lastStrong = QChar::DirR;
00989         }
00990         break;
00991         }
00992     case QChar::DirLRO:
00993         {
00994         uchar level = context->level;
00995         if(level%2) // we have an odd level
00996             level++;
00997         else
00998             level += 2;
00999         if(level < 61) {
01000             runs->append( new KoTextRun(sor, eor, context, dir) );
01001             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01002             context = new KoBidiContext(level, QChar::DirL, context, TRUE);
01003             dir = QChar::DirL;
01004             status.last = QChar::DirL;
01005             status.lastStrong = QChar::DirL;
01006         }
01007         break;
01008         }
01009     case QChar::DirPDF:
01010         {
01011         KoBidiContext *c = context->parent;
01012         if(c) {
01013             runs->append( new KoTextRun(sor, eor, context, dir) );
01014             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01015             status.last = context->dir;
01016             if( context->deref() ) delete context;
01017             context = c;
01018             if(context->override)
01019             dir = context->dir;
01020             else
01021             dir = QChar::DirON;
01022             status.lastStrong = context->dir;
01023         }
01024         break;
01025         }
01026 
01027         // strong types
01028     case QChar::DirL:
01029         if(dir == QChar::DirON)
01030         dir = QChar::DirL;
01031         switch(status.last)
01032         {
01033         case QChar::DirL:
01034             eor = current; status.eor = QChar::DirL; break;
01035         case QChar::DirR:
01036         case QChar::DirAL:
01037         case QChar::DirEN:
01038         case QChar::DirAN:
01039             runs->append( new KoTextRun(sor, eor, context, dir) );
01040             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01041             break;
01042         case QChar::DirES:
01043         case QChar::DirET:
01044         case QChar::DirCS:
01045         case QChar::DirBN:
01046         case QChar::DirB:
01047         case QChar::DirS:
01048         case QChar::DirWS:
01049         case QChar::DirON:
01050             if(dir != QChar::DirL) {
01051             //last stuff takes embedding dir
01052             if( context->dir == QChar::DirR ) {
01053                 if(status.eor != QChar::DirR) {
01054                 // AN or EN
01055                 runs->append( new KoTextRun(sor, eor, context, dir) );
01056                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01057                 dir = QChar::DirR;
01058                 }
01059                 else
01060                 eor = current - 1;
01061                 runs->append( new KoTextRun(sor, eor, context, dir) );
01062                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01063             } else {
01064                 if(status.eor != QChar::DirL) {
01065                 runs->append( new KoTextRun(sor, eor, context, dir) );
01066                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01067                 dir = QChar::DirL;
01068                 } else {
01069                 eor = current; status.eor = QChar::DirL; break;
01070                 }
01071             }
01072             } else {
01073             eor = current; status.eor = QChar::DirL;
01074             }
01075         default:
01076             break;
01077         }
01078         status.lastStrong = QChar::DirL;
01079         break;
01080     case QChar::DirAL:
01081     case QChar::DirR:
01082         if(dir == QChar::DirON) dir = QChar::DirR;
01083         switch(status.last)
01084         {
01085         case QChar::DirR:
01086         case QChar::DirAL:
01087             eor = current; status.eor = QChar::DirR; break;
01088         case QChar::DirL:
01089         case QChar::DirEN:
01090         case QChar::DirAN:
01091             runs->append( new KoTextRun(sor, eor, context, dir) );
01092             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01093             break;
01094         case QChar::DirES:
01095         case QChar::DirET:
01096         case QChar::DirCS:
01097         case QChar::DirBN:
01098         case QChar::DirB:
01099         case QChar::DirS:
01100         case QChar::DirWS:
01101         case QChar::DirON:
01102             if( status.eor != QChar::DirR && status.eor != QChar::DirAL ) {
01103             //last stuff takes embedding dir
01104             if(context->dir == QChar::DirR || status.lastStrong == QChar::DirR) {
01105                 runs->append( new KoTextRun(sor, eor, context, dir) );
01106                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01107                 dir = QChar::DirR;
01108                 eor = current;
01109             } else {
01110                 eor = current - 1;
01111                 runs->append( new KoTextRun(sor, eor, context, dir) );
01112                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01113                 dir = QChar::DirR;
01114             }
01115             } else {
01116             eor = current; status.eor = QChar::DirR;
01117             }
01118         default:
01119             break;
01120         }
01121         status.lastStrong = dirCurrent;
01122         break;
01123 
01124         // weak types:
01125 
01126     case QChar::DirNSM:
01127         // ### if @sor, set dir to dirSor
01128         break;
01129     case QChar::DirEN:
01130         if(status.lastStrong != QChar::DirAL) {
01131         // if last strong was AL change EN to AL
01132         if(dir == QChar::DirON) {
01133             if(status.lastStrong == QChar::DirL)
01134             dir = QChar::DirL;
01135             else
01136             dir = QChar::DirAN;
01137         }
01138         switch(status.last)
01139             {
01140             case QChar::DirET:
01141             if ( status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL ) {
01142                 runs->append( new KoTextRun(sor, eor, context, dir) );
01143                 ++eor; sor = eor; status.eor = QChar::DirON;
01144                 dir = QChar::DirAN;
01145             }
01146             // fall through
01147             case QChar::DirEN:
01148             case QChar::DirL:
01149             eor = current;
01150             status.eor = dirCurrent;
01151             break;
01152             case QChar::DirR:
01153             case QChar::DirAL:
01154             case QChar::DirAN:
01155             runs->append( new KoTextRun(sor, eor, context, dir) );
01156             ++eor; sor = eor; status.eor = QChar::DirEN;
01157             dir = QChar::DirAN; break;
01158             case QChar::DirES:
01159             case QChar::DirCS:
01160             if(status.eor == QChar::DirEN || dir == QChar::DirAN) {
01161                 eor = current; break;
01162             }
01163             case QChar::DirBN:
01164             case QChar::DirB:
01165             case QChar::DirS:
01166             case QChar::DirWS:
01167             case QChar::DirON:
01168             if(status.eor == QChar::DirR) {
01169                 // neutrals go to R
01170                 eor = current - 1;
01171                 runs->append( new KoTextRun(sor, eor, context, dir) );
01172                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirEN;
01173                 dir = QChar::DirAN;
01174             }
01175             else if( status.eor == QChar::DirL ||
01176                  (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
01177                 eor = current; status.eor = dirCurrent;
01178             } else {
01179                 // numbers on both sides, neutrals get right to left direction
01180                 if(dir != QChar::DirL) {
01181                 runs->append( new KoTextRun(sor, eor, context, dir) );
01182                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01183                 eor = current - 1;
01184                 dir = QChar::DirR;
01185                 runs->append( new KoTextRun(sor, eor, context, dir) );
01186                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01187                 dir = QChar::DirAN;
01188                 } else {
01189                 eor = current; status.eor = dirCurrent;
01190                 }
01191             }
01192             default:
01193             break;
01194             }
01195         break;
01196         }
01197     case QChar::DirAN:
01198         dirCurrent = QChar::DirAN;
01199         if(dir == QChar::DirON) dir = QChar::DirAN;
01200         switch(status.last)
01201         {
01202         case QChar::DirL:
01203         case QChar::DirAN:
01204             eor = current; status.eor = QChar::DirAN; break;
01205         case QChar::DirR:
01206         case QChar::DirAL:
01207         case QChar::DirEN:
01208             runs->append( new KoTextRun(sor, eor, context, dir) );
01209             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01210             break;
01211         case QChar::DirCS:
01212             if(status.eor == QChar::DirAN) {
01213             eor = current; status.eor = QChar::DirR; break;
01214             }
01215         case QChar::DirES:
01216         case QChar::DirET:
01217         case QChar::DirBN:
01218         case QChar::DirB:
01219         case QChar::DirS:
01220         case QChar::DirWS:
01221         case QChar::DirON:
01222             if(status.eor == QChar::DirR) {
01223             // neutrals go to R
01224             eor = current - 1;
01225             runs->append( new KoTextRun(sor, eor, context, dir) );
01226             ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01227             dir = QChar::DirAN;
01228             } else if( status.eor == QChar::DirL ||
01229                    (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
01230             eor = current; status.eor = dirCurrent;
01231             } else {
01232             // numbers on both sides, neutrals get right to left direction
01233             if(dir != QChar::DirL) {
01234                 runs->append( new KoTextRun(sor, eor, context, dir) );
01235                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01236                 eor = current - 1;
01237                 dir = QChar::DirR;
01238                 runs->append( new KoTextRun(sor, eor, context, dir) );
01239                 ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
01240                 dir = QChar::DirAN;
01241             } else {
01242                 eor = current; status.eor = dirCurrent;
01243             }
01244             }
01245         default:
01246             break;
01247         }
01248         break;
01249     case QChar::DirES:
01250     case QChar::DirCS:
01251         break;
01252     case QChar::DirET:
01253         if(status.last == QChar::DirEN) {
01254         dirCurrent = QChar::DirEN;
01255         eor = current; status.eor = dirCurrent;
01256         break;
01257         }
01258         break;
01259 
01260         // boundary neutrals should be ignored
01261     case QChar::DirBN:
01262         break;
01263         // neutrals
01264     case QChar::DirB:
01265         // ### what do we do with newline and paragraph separators that come to here?
01266         break;
01267     case QChar::DirS:
01268         // ### implement rule L1
01269         break;
01270     case QChar::DirWS:
01271     case QChar::DirON:
01272         break;
01273     default:
01274         break;
01275     }
01276 
01277     //cout << "     after: dir=" << //        dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
01278 
01279     if(current >= (int)text.length()) break;
01280 
01281     // set status.last as needed.
01282     switch(dirCurrent)
01283         {
01284         case QChar::DirET:
01285         case QChar::DirES:
01286         case QChar::DirCS:
01287         case QChar::DirS:
01288         case QChar::DirWS:
01289         case QChar::DirON:
01290         switch(status.last)
01291             {
01292             case QChar::DirL:
01293             case QChar::DirR:
01294             case QChar::DirAL:
01295             case QChar::DirEN:
01296             case QChar::DirAN:
01297             status.last = dirCurrent;
01298             break;
01299             default:
01300             status.last = QChar::DirON;
01301             }
01302         break;
01303         case QChar::DirNSM:
01304         case QChar::DirBN:
01305         // ignore these
01306         break;
01307         default:
01308         status.last = dirCurrent;
01309         }
01310 
01311     ++current;
01312     }
01313 
01314 #if (BIDI_DEBUG >= 1)
01315     cout << "reached end of line current=" << current << ", eor=" << eor << endl;
01316 #endif
01317     eor = current - 1; // remove dummy char
01318 
01319     if ( sor <= eor )
01320     runs->append( new KoTextRun(sor, eor, context, dir) );
01321 
01322     // reorder line according to run structure...
01323 
01324     // first find highest and lowest levels
01325     uchar levelLow = 128;
01326     uchar levelHigh = 0;
01327     KoTextRun *r = runs->first();
01328     while ( r ) {
01329     //printf("level = %d\n", r->level);
01330     if ( r->level > levelHigh )
01331         levelHigh = r->level;
01332     if ( r->level < levelLow )
01333         levelLow = r->level;
01334     r = runs->next();
01335     }
01336 
01337     // implements reordering of the line (L2 according to BiDi spec):
01338     // L2. From the highest level found in the text to the lowest odd level on each line,
01339     // reverse any contiguous sequence of characters that are at that level or higher.
01340 
01341     // reversing is only done up to the lowest odd level
01342     if(!(levelLow%2)) levelLow++;
01343 
01344 #if (BIDI_DEBUG >= 1)
01345     cout << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
01346     cout << "logical order is:" << endl;
01347     QPtrListIterator<KoTextRun> it2(*runs);
01348     KoTextRun *r2;
01349     for ( ; (r2 = it2.current()); ++it2 )
01350     cout << "    " << r2 << "  start=" << r2->start << "  stop=" << r2->stop << "  level=" << (uint)r2->level << endl;
01351 #endif
01352 
01353     int count = runs->count() - 1;
01354 
01355     while(levelHigh >= levelLow)
01356     {
01357     int i = 0;
01358     while ( i < count )
01359     {
01360         while(i < count && runs->at(i)->level < levelHigh) i++;
01361         int start = i;
01362         while(i <= count && runs->at(i)->level >= levelHigh) i++;
01363         int end = i-1;
01364 
01365         if(start != end)
01366         {
01367         //cout << "reversing from " << start << " to " << end << endl;
01368         for(int j = 0; j < (end-start+1)/2; j++)
01369         {
01370             KoTextRun *first = runs->take(start+j);
01371             KoTextRun *last = runs->take(end-j-1);
01372             runs->insert(start+j, last);
01373             runs->insert(end-j, first);
01374         }
01375         }
01376         i++;
01377         if(i >= count) break;
01378     }
01379     levelHigh--;
01380     }
01381 
01382 #if (BIDI_DEBUG >= 1)
01383     cout << "visual order is:" << endl;
01384     QPtrListIterator<KoTextRun> it3(*runs);
01385     KoTextRun *r3;
01386     for ( ; (r3 = it3.current()); ++it3 )
01387     {
01388     cout << "    " << r3 << endl;
01389     }
01390 #endif
01391 
01392     control->setContext( context );
01393     control->status = status;
01394 
01395     return runs;
01396 }
01397 
01398 
01399 QString KoComplexText::bidiReorderString( const QString &str, QChar::Direction /*basicDir*/ )
01400 {
01401 
01402 // ### fix basic direction
01403     KoBidiControl control;
01404     int lineStart = 0;
01405     int lineEnd = 0;
01406     int len = str.length();
01407     QString visual;
01408     visual.setUnicode( 0, len );
01409     QChar *vch = (QChar *)visual.unicode();
01410     const QChar *ch = str.unicode();
01411     while( lineStart < len ) {
01412     lineEnd = lineStart;
01413     while( *ch != '\n' && lineEnd < len ) {
01414         ch++;
01415         lineEnd++;
01416     }
01417     lineEnd++;
01418     QPtrList<KoTextRun> *runs = bidiReorderLine( &control, str, lineStart, lineEnd - lineStart );
01419 
01420     // reorder the content of the line, and output to visual
01421     KoTextRun *r = runs->first();
01422     while ( r ) {
01423         if(r->level %2) {
01424         // odd level, need to reverse the string
01425         int pos = r->stop;
01426         while(pos >= r->start) {
01427             *vch = str[pos];
01428             if ( vch->mirrored() )
01429             *vch = vch->mirroredChar();
01430             vch++;
01431             pos--;
01432         }
01433         } else {
01434         int pos = r->start;
01435         while(pos <= r->stop) {
01436             *vch = str[pos];
01437             vch++;
01438             pos++;
01439         }
01440         }
01441         r = runs->next();
01442     }
01443     if ( *ch == '\n' ) {
01444         *vch = *ch;
01445         vch++;
01446         ch++;
01447         lineEnd++;
01448     }
01449     lineStart = lineEnd;
01450     }
01451     return visual;
01452 }
01453 
01454 KoTextRun::KoTextRun(int _start, int _stop, KoBidiContext *context, QChar::Direction dir) {
01455     start = _start;
01456     stop = _stop;
01457     if(dir == QChar::DirON) dir = context->dir;
01458 
01459     level = context->level;
01460 
01461     // add level of run (cases I1 & I2)
01462     if( level % 2 ) {
01463     if(dir == QChar::DirL || dir == QChar::DirAN)
01464         level++;
01465     } else {
01466     if( dir == QChar::DirR )
01467         level++;
01468     else if( dir == QChar::DirAN )
01469         level += 2;
01470     }
01471 #if (BIDI_DEBUG >= 1)
01472     printf("new run: dir=%d from %d, to %d level = %d\n", dir, _start, _stop, level);
01473 #endif
01474 }
01475 
01476 #endif //QT_NO_COMPLEXTEXT
KDE Logo
This file is part of the documentation for lib Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:40:15 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003