00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #ifndef QT_NO_COMPLEXTEXT
00040 #include "KoRichText.h"
00041
00042 #include "qfontmetrics.h"
00043 #include "qrect.h"
00044
00045 #include <Q3PointArray>
00046 #include <Q3PtrList>
00047
00048 #include <stdlib.h>
00049
00050
00051
00052
00053
00054
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
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 static inline const QChar *prevChar( const QString &str, int pos )
00116 {
00117
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
00136 if( !ch->isMark() )
00137 return ch;
00138
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
00160 QChar::Joining joining = str[pos].joining();
00161
00162 switch ( joining ) {
00163 case QChar::OtherJoining:
00164 case QChar::Center:
00165
00166 return XIsolated;
00167 case QChar::Right:
00168
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
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
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
00205 QChar::Joining joining = str[pos].joining();
00206
00207 switch ( joining ) {
00208 case QChar::OtherJoining:
00209 case QChar::Center:
00210
00211 return XIsolated;
00212 case QChar::Right:
00213
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
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
00236
00237
00238 static const ushort arabicUnicodeMapping[256][2] = {
00239
00240
00241 { 0x0600, 0 },
00242 { 0x0601, 0 },
00243 { 0x0602, 0 },
00244 { 0x0603, 0 },
00245 { 0x0604, 0 },
00246 { 0x0605, 0 },
00247 { 0x0606, 0 },
00248 { 0x0607, 0 },
00249 { 0x0608, 0 },
00250 { 0x0609, 0 },
00251 { 0x060a, 0 },
00252 { 0x060b, 0 },
00253 { 0x060c, 0 },
00254 { 0x060d, 0 },
00255 { 0x060e, 0 },
00256 { 0x060f, 0 },
00257
00258 { 0x0610, 0 },
00259 { 0x0611, 0 },
00260 { 0x0612, 0 },
00261 { 0x0613, 0 },
00262 { 0x0614, 0 },
00263 { 0x0615, 0 },
00264 { 0x0616, 0 },
00265 { 0x0617, 0 },
00266 { 0x0618, 0 },
00267 { 0x0619, 0 },
00268 { 0x061a, 0 },
00269 { 0x061b, 0 },
00270 { 0x061c, 0 },
00271 { 0x061d, 0 },
00272 { 0x061e, 0 },
00273 { 0x061f, 0 },
00274
00275 { 0x0620, 0 },
00276 { 0xfe80, 0 },
00277 { 0xfe81, 1 },
00278 { 0xfe83, 1 },
00279 { 0xfe85, 1 },
00280 { 0xfe87, 1 },
00281 { 0xfe89, 3 },
00282 { 0xfe8d, 1 },
00283 { 0xfe8f, 3 },
00284 { 0xfe93, 1 },
00285 { 0xfe95, 3 },
00286 { 0xfe99, 3 },
00287 { 0xfe9d, 3 },
00288 { 0xfea1, 3 },
00289 { 0xfea5, 3 },
00290 { 0xfea9, 1 },
00291
00292 { 0xfeab, 1 },
00293 { 0xfead, 1 },
00294 { 0xfeaf, 1 },
00295 { 0xfeb1, 1 },
00296 { 0xfeb5, 3 },
00297 { 0xfeb9, 3 },
00298 { 0xfebd, 3 },
00299 { 0xfec1, 3 },
00300 { 0xfec5, 3 },
00301 { 0xfec9, 3 },
00302 { 0xfecd, 3 },
00303 { 0x063b, 0 },
00304 { 0x063c, 0 },
00305 { 0x063d, 0 },
00306 { 0x063e, 0 },
00307 { 0x063f, 0 },
00308
00309 { 0x0640, 0 },
00310 { 0xfed1, 3 },
00311 { 0xfed5, 3 },
00312 { 0xfed9, 3 },
00313 { 0xfedd, 3 },
00314 { 0xfee1, 3 },
00315 { 0xfee5, 3 },
00316 { 0xfee9, 3 },
00317 { 0xfeed, 1 },
00318 { 0xfeef, 1 },
00319 { 0xfef1, 3 },
00320 { 0x064b, 0 },
00321 { 0x064c, 0 },
00322 { 0x064d, 0 },
00323 { 0x064e, 0 },
00324 { 0x064f, 0 },
00325
00326 { 0x0650, 0 },
00327 { 0x0651, 0 },
00328 { 0x0652, 0 },
00329
00330 { 0x0653, 0 },
00331 { 0x0654, 0 },
00332 { 0x0655, 0 },
00333 { 0x0656, 0 },
00334 { 0x0657, 0 },
00335 { 0x0658, 0 },
00336 { 0x0659, 0 },
00337 { 0x065a, 0 },
00338 { 0x065b, 0 },
00339 { 0x065c, 0 },
00340 { 0x065d, 0 },
00341 { 0x065e, 0 },
00342 { 0x065f, 0 },
00343
00344 { 0x0660, 0 },
00345 { 0x0661, 0 },
00346 { 0x0662, 0 },
00347 { 0x0663, 0 },
00348 { 0x0664, 0 },
00349 { 0x0665, 0 },
00350 { 0x0666, 0 },
00351 { 0x0667, 0 },
00352 { 0x0668, 0 },
00353 { 0x0669, 0 },
00354 { 0x066a, 0 },
00355 { 0x066b, 0 },
00356 { 0x066c, 0 },
00357 { 0x066d, 0 },
00358 { 0x066e, 0 },
00359 { 0x066f, 0 },
00360
00361
00362
00363 { 0x0670, 0 },
00364 { 0xfb50, 1 },
00365 { 0x0672, 0 },
00366 { 0x0673, 0 },
00367 { 0x0674, 0 },
00368 { 0x0675, 0 },
00369 { 0x0676, 0 },
00370 { 0xfbdd, 0 },
00371 { 0x0678, 0 },
00372 { 0xfb66, 3 },
00373 { 0xfb5e, 3 },
00374 { 0xfb52, 3 },
00375 { 0x067c, 0 },
00376 { 0x067d, 0 },
00377 { 0xfb56, 3 },
00378 { 0xfb62, 3 },
00379
00380 { 0xfb5a, 3 },
00381 { 0x0681, 0 },
00382 { 0x0682, 0 },
00383 { 0xfb76, 3 },
00384 { 0xfb72, 3 },
00385 { 0x0685, 0 },
00386 { 0xfb7a, 3 },
00387 { 0xfb7e, 3 },
00388 { 0xfb88, 1 },
00389 { 0x0689, 0 },
00390 { 0x068a, 0 },
00391 { 0x068b, 0 },
00392 { 0xfb84, 1 },
00393 { 0xfb82, 1 },
00394 { 0xfb86, 1 },
00395 { 0x068f, 0 },
00396
00397 { 0x0690, 0 },
00398 { 0xfb8c, 1 },
00399 { 0x0692, 0 },
00400 { 0x0693, 0 },
00401 { 0x0694, 0 },
00402 { 0x0695, 0 },
00403 { 0x0696, 0 },
00404 { 0x0697, 0 },
00405 { 0xfb8a, 1 },
00406 { 0x0699, 0 },
00407 { 0x069a, 0 },
00408 { 0x069b, 0 },
00409 { 0x069c, 0 },
00410 { 0x069d, 0 },
00411 { 0x069e, 0 },
00412 { 0x069f, 0 },
00413
00414 { 0x06a0, 0 },
00415 { 0x06a1, 0 },
00416 { 0x06a2, 0 },
00417 { 0x06a3, 0 },
00418 { 0xfb6a, 3 },
00419 { 0x06a5, 0 },
00420 { 0xfb6e, 3 },
00421 { 0x06a7, 0 },
00422 { 0x06a8, 0 },
00423 { 0xfb8e, 3 },
00424 { 0x06aa, 0 },
00425 { 0x06ab, 0 },
00426 { 0x06ac, 0 },
00427 { 0xfbd3, 3 },
00428 { 0x06ae, 0 },
00429 { 0xfb92, 3 },
00430
00431 { 0x06b0, 0 },
00432 { 0xfb9a, 3 },
00433 { 0x06b2, 0 },
00434 { 0xfb96, 3 },
00435 { 0x06b4, 0 },
00436 { 0x06b5, 0 },
00437 { 0x06b6, 0 },
00438 { 0x06b7, 0 },
00439 { 0x06b8, 0 },
00440 { 0x06b9, 0 },
00441 { 0xfb9e, 1 },
00442 { 0xfba0, 3 },
00443 { 0x06bc, 0 },
00444 { 0x06bd, 0 },
00445 { 0xfbaa, 3 },
00446 { 0x06bf, 0 },
00447
00448 { 0xfba4, 1 },
00449 { 0xfba6, 3 },
00450 { 0x06c2, 0 },
00451 { 0x06c3, 0 },
00452 { 0x06c4, 0 },
00453 { 0xfbe0, 1 },
00454 { 0xfbd9, 1 },
00455 { 0xfbd7, 1 },
00456 { 0xfbdb, 1 },
00457 { 0xfbe2, 1 },
00458 { 0x06ca, 0 },
00459 { 0xfbde, 1 },
00460 { 0x06cc, 0 },
00461 { 0x06cd, 0 },
00462 { 0x06ce, 0 },
00463 { 0x06cf, 0 },
00464
00465 { 0xfbe4, 3 },
00466 { 0x06d1, 0 },
00467 { 0xfbae, 1 },
00468 { 0xfbb0, 1 },
00469 { 0x06d4, 0 },
00470 { 0x06d5, 0 },
00471 { 0x06d6, 0 },
00472 { 0x06d7, 0 },
00473 { 0x06d8, 0 },
00474 { 0x06d9, 0 },
00475 { 0x06da, 0 },
00476 { 0x06db, 0 },
00477 { 0x06dc, 0 },
00478 { 0x06dd, 0 },
00479 { 0x06de, 0 },
00480 { 0x06df, 0 },
00481
00482 { 0x06e0, 0 },
00483 { 0x06e1, 0 },
00484 { 0x06e2, 0 },
00485 { 0x06e3, 0 },
00486 { 0x06e4, 0 },
00487 { 0x06e5, 0 },
00488 { 0x06e6, 0 },
00489 { 0x06e7, 0 },
00490 { 0x06e8, 0 },
00491 { 0x06e9, 0 },
00492 { 0x06ea, 0 },
00493 { 0x06eb, 0 },
00494 { 0x06ec, 0 },
00495 { 0x06ed, 0 },
00496 { 0x06ee, 0 },
00497 { 0x06ef, 0 },
00498
00499 { 0x06f0, 0 },
00500 { 0x06f1, 0 },
00501 { 0x06f2, 0 },
00502 { 0x06f3, 0 },
00503 { 0x06f4, 0 },
00504 { 0x06f5, 0 },
00505 { 0x06f6, 0 },
00506 { 0x06f7, 0 },
00507 { 0x06f8, 0 },
00508 { 0x06f9, 0 },
00509 { 0x06fa, 0 },
00510 { 0x06fb, 0 },
00511 { 0x06fc, 0 },
00512 { 0x06fd, 0 },
00513 { 0x06fe, 0 },
00514 { 0x06ff, 0 },
00515
00516 };
00517
00518
00519
00520
00521 static const ushort arabicUnicodeLamAlefMapping[6][4] = {
00522 { 0xfffd, 0xfffd, 0xfef5, 0xfef6 },
00523 { 0xfffd, 0xfffd, 0xfef7, 0xfef8 },
00524 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
00525 { 0xfffd, 0xfffd, 0xfef9, 0xfefa },
00526 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
00527 { 0xfffd, 0xfffd, 0xfefb, 0xfefc }
00528 };
00529
00530 static inline int getShape( const QChar * , uchar cell, int shape,
00531 const QFontMetrics * )
00532 {
00533 uint ch = arabicUnicodeMapping[cell][0] + shape;
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
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
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
00585
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
00609
00610 ushort map;
00611 switch ( c ) {
00612 case 0x44: {
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
00621 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
00622 goto next;
00623 default:
00624 break;
00625 }
00626 }
00627 break;
00628 }
00629 case 0x22:
00630 case 0x23:
00631 case 0x25:
00632 case 0x27:
00633 if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
00634
00635
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
00659 QChar *s = shapeBuffer;
00660 int i = 0;
00661 while ( i < lenOut ) {
00662 if ( s->combiningClass() != 0 ) {
00663
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
00701
00702 switch ( ch->cell() ) {
00703 case 0x44: {
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:
00719 case 0x23:
00720 case 0x25:
00721 case 0x27:
00722 if ( prevChar( str, pos )->unicode() == 0x0644 )
00723
00724 return QChar(0);
00725 default:
00726 break;
00727 }
00728 return QChar( getShape( ch, ch->cell(), shape, fm ) );
00729 }
00730 }
00731
00732
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
00750 int offset = f->actual.pixelSize / 10 + 1;
00751
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
00763
00764
00765
00766 if ( cmb >= 27 && cmb <= 36 )
00767 offset +=1;
00768
00769 if ( (cmb >= 10 && cmb <= 18) ||
00770 cmb == 20 || cmb == 22 ||
00771 cmb == 29 || cmb == 32 )
00772 cmb = QChar::Combining_Below;
00773
00774 else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
00775 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
00776 cmb = QChar::Combining_Above;
00777
00778 else if ( cmb == 103 )
00779 cmb = QChar::Combining_BelowRight;
00780
00781 else if ( cmb == 24 || cmb == 107 )
00782 cmb = QChar::Combining_AboveRight;
00783 else if ( cmb == 25 )
00784 cmb = QChar::Combining_AboveLeft;
00785
00786
00787
00788 }
00789
00790
00791 if ( cmb != lastCmb ) {
00792
00793 attachmentRect = baseRect;
00794 }
00795
00796 QPoint p;
00797 QRect markRect = f->boundingRect( mark );
00798 switch( cmb ) {
00799 case QChar::Combining_DoubleBelow:
00800
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
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
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
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
00895
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
00901
00902 Q3PtrList<KoTextRun> *runs = new Q3PtrList<KoTextRun>;
00903 runs->setAutoDelete(true);
00904
00905 KoBidiContext *context = control->context;
00906 if ( !context ) {
00907
00908
00909
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
00946 case QChar::DirRLE:
00947 {
00948 uchar level = context->level;
00949 if(level%2)
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)
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)
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)
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
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
01055 if( context->dir == QChar::DirR ) {
01056 if(status.eor != QChar::DirR) {
01057
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
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
01128
01129 case QChar::DirNSM:
01130
01131 break;
01132 case QChar::DirEN:
01133 if(status.lastStrong != QChar::DirAL) {
01134
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
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
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
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
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
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
01264 case QChar::DirBN:
01265 break;
01266
01267 case QChar::DirB:
01268
01269 break;
01270 case QChar::DirS:
01271
01272 break;
01273 case QChar::DirWS:
01274 case QChar::DirON:
01275 break;
01276 default:
01277 break;
01278 }
01279
01280
01281
01282 if(current >= (int)text.length()) break;
01283
01284
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
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;
01321
01322 if ( sor <= eor )
01323 runs->append( new KoTextRun(sor, eor, context, dir) );
01324
01325
01326
01327
01328 uchar levelLow = 128;
01329 uchar levelHigh = 0;
01330 KoTextRun *r = runs->first();
01331 while ( r ) {
01332
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
01341
01342
01343
01344
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
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 )
01403 {
01404
01405
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
01424 KoTextRun *r = runs->first();
01425 while ( r ) {
01426 if(r->level %2) {
01427
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
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