lib Library API Documentation

qcomplextext.cpp

00001 /**************************************************************************** 00002 ** $Id: qcomplextext.cpp,v 1.10 2003/06/01 23:39:23 faure Exp $ 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.3.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Sep 24 18:22:28 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003