F:/KPlato/koffice/libs/kotext/KoComplexText.cpp

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

Généré le Wed Nov 22 23:41:07 2006 pour KPlato par  doxygen 1.5.1-p1