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 <stdlib.h>
00046
00047
00048
00049
00050
00051
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
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 static inline const QChar *prevChar( const QString &str, int pos )
00113 {
00114
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
00133 if( !ch->isMark() )
00134 return ch;
00135
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
00157 QChar::Joining joining = str[pos].joining();
00158
00159 switch ( joining ) {
00160 case QChar::OtherJoining:
00161 case QChar::Center:
00162
00163 return XIsolated;
00164 case QChar::Right:
00165
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
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
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
00202 QChar::Joining joining = str[pos].joining();
00203
00204 switch ( joining ) {
00205 case QChar::OtherJoining:
00206 case QChar::Center:
00207
00208 return XIsolated;
00209 case QChar::Right:
00210
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
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
00233
00234
00235 static const ushort arabicUnicodeMapping[256][2] = {
00236
00237
00238 { 0x0600, 0 },
00239 { 0x0601, 0 },
00240 { 0x0602, 0 },
00241 { 0x0603, 0 },
00242 { 0x0604, 0 },
00243 { 0x0605, 0 },
00244 { 0x0606, 0 },
00245 { 0x0607, 0 },
00246 { 0x0608, 0 },
00247 { 0x0609, 0 },
00248 { 0x060a, 0 },
00249 { 0x060b, 0 },
00250 { 0x060c, 0 },
00251 { 0x060d, 0 },
00252 { 0x060e, 0 },
00253 { 0x060f, 0 },
00254
00255 { 0x0610, 0 },
00256 { 0x0611, 0 },
00257 { 0x0612, 0 },
00258 { 0x0613, 0 },
00259 { 0x0614, 0 },
00260 { 0x0615, 0 },
00261 { 0x0616, 0 },
00262 { 0x0617, 0 },
00263 { 0x0618, 0 },
00264 { 0x0619, 0 },
00265 { 0x061a, 0 },
00266 { 0x061b, 0 },
00267 { 0x061c, 0 },
00268 { 0x061d, 0 },
00269 { 0x061e, 0 },
00270 { 0x061f, 0 },
00271
00272 { 0x0620, 0 },
00273 { 0xfe80, 0 },
00274 { 0xfe81, 1 },
00275 { 0xfe83, 1 },
00276 { 0xfe85, 1 },
00277 { 0xfe87, 1 },
00278 { 0xfe89, 3 },
00279 { 0xfe8d, 1 },
00280 { 0xfe8f, 3 },
00281 { 0xfe93, 1 },
00282 { 0xfe95, 3 },
00283 { 0xfe99, 3 },
00284 { 0xfe9d, 3 },
00285 { 0xfea1, 3 },
00286 { 0xfea5, 3 },
00287 { 0xfea9, 1 },
00288
00289 { 0xfeab, 1 },
00290 { 0xfead, 1 },
00291 { 0xfeaf, 1 },
00292 { 0xfeb1, 1 },
00293 { 0xfeb5, 3 },
00294 { 0xfeb9, 3 },
00295 { 0xfebd, 3 },
00296 { 0xfec1, 3 },
00297 { 0xfec5, 3 },
00298 { 0xfec9, 3 },
00299 { 0xfecd, 3 },
00300 { 0x063b, 0 },
00301 { 0x063c, 0 },
00302 { 0x063d, 0 },
00303 { 0x063e, 0 },
00304 { 0x063f, 0 },
00305
00306 { 0x0640, 0 },
00307 { 0xfed1, 3 },
00308 { 0xfed5, 3 },
00309 { 0xfed9, 3 },
00310 { 0xfedd, 3 },
00311 { 0xfee1, 3 },
00312 { 0xfee5, 3 },
00313 { 0xfee9, 3 },
00314 { 0xfeed, 1 },
00315 { 0xfeef, 1 },
00316 { 0xfef1, 3 },
00317 { 0x064b, 0 },
00318 { 0x064c, 0 },
00319 { 0x064d, 0 },
00320 { 0x064e, 0 },
00321 { 0x064f, 0 },
00322
00323 { 0x0650, 0 },
00324 { 0x0651, 0 },
00325 { 0x0652, 0 },
00326
00327 { 0x0653, 0 },
00328 { 0x0654, 0 },
00329 { 0x0655, 0 },
00330 { 0x0656, 0 },
00331 { 0x0657, 0 },
00332 { 0x0658, 0 },
00333 { 0x0659, 0 },
00334 { 0x065a, 0 },
00335 { 0x065b, 0 },
00336 { 0x065c, 0 },
00337 { 0x065d, 0 },
00338 { 0x065e, 0 },
00339 { 0x065f, 0 },
00340
00341 { 0x0660, 0 },
00342 { 0x0661, 0 },
00343 { 0x0662, 0 },
00344 { 0x0663, 0 },
00345 { 0x0664, 0 },
00346 { 0x0665, 0 },
00347 { 0x0666, 0 },
00348 { 0x0667, 0 },
00349 { 0x0668, 0 },
00350 { 0x0669, 0 },
00351 { 0x066a, 0 },
00352 { 0x066b, 0 },
00353 { 0x066c, 0 },
00354 { 0x066d, 0 },
00355 { 0x066e, 0 },
00356 { 0x066f, 0 },
00357
00358
00359
00360 { 0x0670, 0 },
00361 { 0xfb50, 1 },
00362 { 0x0672, 0 },
00363 { 0x0673, 0 },
00364 { 0x0674, 0 },
00365 { 0x0675, 0 },
00366 { 0x0676, 0 },
00367 { 0xfbdd, 0 },
00368 { 0x0678, 0 },
00369 { 0xfb66, 3 },
00370 { 0xfb5e, 3 },
00371 { 0xfb52, 3 },
00372 { 0x067c, 0 },
00373 { 0x067d, 0 },
00374 { 0xfb56, 3 },
00375 { 0xfb62, 3 },
00376
00377 { 0xfb5a, 3 },
00378 { 0x0681, 0 },
00379 { 0x0682, 0 },
00380 { 0xfb76, 3 },
00381 { 0xfb72, 3 },
00382 { 0x0685, 0 },
00383 { 0xfb7a, 3 },
00384 { 0xfb7e, 3 },
00385 { 0xfb88, 1 },
00386 { 0x0689, 0 },
00387 { 0x068a, 0 },
00388 { 0x068b, 0 },
00389 { 0xfb84, 1 },
00390 { 0xfb82, 1 },
00391 { 0xfb86, 1 },
00392 { 0x068f, 0 },
00393
00394 { 0x0690, 0 },
00395 { 0xfb8c, 1 },
00396 { 0x0692, 0 },
00397 { 0x0693, 0 },
00398 { 0x0694, 0 },
00399 { 0x0695, 0 },
00400 { 0x0696, 0 },
00401 { 0x0697, 0 },
00402 { 0xfb8a, 1 },
00403 { 0x0699, 0 },
00404 { 0x069a, 0 },
00405 { 0x069b, 0 },
00406 { 0x069c, 0 },
00407 { 0x069d, 0 },
00408 { 0x069e, 0 },
00409 { 0x069f, 0 },
00410
00411 { 0x06a0, 0 },
00412 { 0x06a1, 0 },
00413 { 0x06a2, 0 },
00414 { 0x06a3, 0 },
00415 { 0xfb6a, 3 },
00416 { 0x06a5, 0 },
00417 { 0xfb6e, 3 },
00418 { 0x06a7, 0 },
00419 { 0x06a8, 0 },
00420 { 0xfb8e, 3 },
00421 { 0x06aa, 0 },
00422 { 0x06ab, 0 },
00423 { 0x06ac, 0 },
00424 { 0xfbd3, 3 },
00425 { 0x06ae, 0 },
00426 { 0xfb92, 3 },
00427
00428 { 0x06b0, 0 },
00429 { 0xfb9a, 3 },
00430 { 0x06b2, 0 },
00431 { 0xfb96, 3 },
00432 { 0x06b4, 0 },
00433 { 0x06b5, 0 },
00434 { 0x06b6, 0 },
00435 { 0x06b7, 0 },
00436 { 0x06b8, 0 },
00437 { 0x06b9, 0 },
00438 { 0xfb9e, 1 },
00439 { 0xfba0, 3 },
00440 { 0x06bc, 0 },
00441 { 0x06bd, 0 },
00442 { 0xfbaa, 3 },
00443 { 0x06bf, 0 },
00444
00445 { 0xfba4, 1 },
00446 { 0xfba6, 3 },
00447 { 0x06c2, 0 },
00448 { 0x06c3, 0 },
00449 { 0x06c4, 0 },
00450 { 0xfbe0, 1 },
00451 { 0xfbd9, 1 },
00452 { 0xfbd7, 1 },
00453 { 0xfbdb, 1 },
00454 { 0xfbe2, 1 },
00455 { 0x06ca, 0 },
00456 { 0xfbde, 1 },
00457 { 0x06cc, 0 },
00458 { 0x06cd, 0 },
00459 { 0x06ce, 0 },
00460 { 0x06cf, 0 },
00461
00462 { 0xfbe4, 3 },
00463 { 0x06d1, 0 },
00464 { 0xfbae, 1 },
00465 { 0xfbb0, 1 },
00466 { 0x06d4, 0 },
00467 { 0x06d5, 0 },
00468 { 0x06d6, 0 },
00469 { 0x06d7, 0 },
00470 { 0x06d8, 0 },
00471 { 0x06d9, 0 },
00472 { 0x06da, 0 },
00473 { 0x06db, 0 },
00474 { 0x06dc, 0 },
00475 { 0x06dd, 0 },
00476 { 0x06de, 0 },
00477 { 0x06df, 0 },
00478
00479 { 0x06e0, 0 },
00480 { 0x06e1, 0 },
00481 { 0x06e2, 0 },
00482 { 0x06e3, 0 },
00483 { 0x06e4, 0 },
00484 { 0x06e5, 0 },
00485 { 0x06e6, 0 },
00486 { 0x06e7, 0 },
00487 { 0x06e8, 0 },
00488 { 0x06e9, 0 },
00489 { 0x06ea, 0 },
00490 { 0x06eb, 0 },
00491 { 0x06ec, 0 },
00492 { 0x06ed, 0 },
00493 { 0x06ee, 0 },
00494 { 0x06ef, 0 },
00495
00496 { 0x06f0, 0 },
00497 { 0x06f1, 0 },
00498 { 0x06f2, 0 },
00499 { 0x06f3, 0 },
00500 { 0x06f4, 0 },
00501 { 0x06f5, 0 },
00502 { 0x06f6, 0 },
00503 { 0x06f7, 0 },
00504 { 0x06f8, 0 },
00505 { 0x06f9, 0 },
00506 { 0x06fa, 0 },
00507 { 0x06fb, 0 },
00508 { 0x06fc, 0 },
00509 { 0x06fd, 0 },
00510 { 0x06fe, 0 },
00511 { 0x06ff, 0 },
00512
00513 };
00514
00515
00516
00517
00518 static const ushort arabicUnicodeLamAlefMapping[6][4] = {
00519 { 0xfffd, 0xfffd, 0xfef5, 0xfef6 },
00520 { 0xfffd, 0xfffd, 0xfef7, 0xfef8 },
00521 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
00522 { 0xfffd, 0xfffd, 0xfef9, 0xfefa },
00523 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
00524 { 0xfffd, 0xfffd, 0xfefb, 0xfefc }
00525 };
00526
00527 static inline int getShape( const QChar * , uchar cell, int shape,
00528 const QFontMetrics * )
00529 {
00530 uint ch = arabicUnicodeMapping[cell][0] + shape;
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
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
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
00582
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
00606
00607 ushort map;
00608 switch ( c ) {
00609 case 0x44: {
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
00618 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
00619 goto next;
00620 default:
00621 break;
00622 }
00623 }
00624 break;
00625 }
00626 case 0x22:
00627 case 0x23:
00628 case 0x25:
00629 case 0x27:
00630 if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
00631
00632
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
00656 QChar *s = shapeBuffer;
00657 int i = 0;
00658 while ( i < lenOut ) {
00659 if ( s->combiningClass() != 0 ) {
00660
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
00698
00699 switch ( ch->cell() ) {
00700 case 0x44: {
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:
00716 case 0x23:
00717 case 0x25:
00718 case 0x27:
00719 if ( prevChar( str, pos )->unicode() == 0x0644 )
00720
00721 return QChar(0);
00722 default:
00723 break;
00724 }
00725 return QChar( getShape( ch, ch->cell(), shape, fm ) );
00726 }
00727 }
00728
00729
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
00747 int offset = f->actual.pixelSize / 10 + 1;
00748
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
00760
00761
00762
00763 if ( cmb >= 27 && cmb <= 36 )
00764 offset +=1;
00765
00766 if ( (cmb >= 10 && cmb <= 18) ||
00767 cmb == 20 || cmb == 22 ||
00768 cmb == 29 || cmb == 32 )
00769 cmb = QChar::Combining_Below;
00770
00771 else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
00772 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
00773 cmb = QChar::Combining_Above;
00774
00775 else if ( cmb == 103 )
00776 cmb = QChar::Combining_BelowRight;
00777
00778 else if ( cmb == 24 || cmb == 107 )
00779 cmb = QChar::Combining_AboveRight;
00780 else if ( cmb == 25 )
00781 cmb = QChar::Combining_AboveLeft;
00782
00783
00784
00785 }
00786
00787
00788 if ( cmb != lastCmb ) {
00789
00790 attachmentRect = baseRect;
00791 }
00792
00793 QPoint p;
00794 QRect markRect = f->boundingRect( mark );
00795 switch( cmb ) {
00796 case QChar::Combining_DoubleBelow:
00797
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
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
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
00859 #ifdef BIDI_DEBUG
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
00892
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
00898
00899 QPtrList<KoTextRun> *runs = new QPtrList<KoTextRun>;
00900 runs->setAutoDelete(TRUE);
00901
00902 KoBidiContext *context = control->context;
00903 if ( !context ) {
00904
00905
00906
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 #ifdef BIDI_DEBUG
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
00943 case QChar::DirRLE:
00944 {
00945 uchar level = context->level;
00946 if(level%2)
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)
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)
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)
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
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
01052 if( context->dir == QChar::DirR ) {
01053 if(status.eor != QChar::DirR) {
01054
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
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
01125
01126 case QChar::DirNSM:
01127
01128 break;
01129 case QChar::DirEN:
01130 if(status.lastStrong != QChar::DirAL) {
01131
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
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
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
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
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
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
01261 case QChar::DirBN:
01262 break;
01263
01264 case QChar::DirB:
01265
01266 break;
01267 case QChar::DirS:
01268
01269 break;
01270 case QChar::DirWS:
01271 case QChar::DirON:
01272 break;
01273 default:
01274 break;
01275 }
01276
01277
01278
01279 if(current >= (int)text.length()) break;
01280
01281
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
01306 break;
01307 default:
01308 status.last = dirCurrent;
01309 }
01310
01311 ++current;
01312 }
01313
01314 #ifdef BIDI_DEBUG
01315 cout << "reached end of line current=" << current << ", eor=" << eor << endl;
01316 #endif
01317 eor = current - 1;
01318
01319 if ( sor <= eor )
01320 runs->append( new KoTextRun(sor, eor, context, dir) );
01321
01322
01323
01324
01325 uchar levelLow = 128;
01326 uchar levelHigh = 0;
01327 KoTextRun *r = runs->first();
01328 while ( r ) {
01329
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
01338
01339
01340
01341
01342 if(!(levelLow%2)) levelLow++;
01343
01344 #ifdef BIDI_DEBUG
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
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 #ifdef BIDI_DEBUG
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 )
01400 {
01401
01402
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
01421 KoTextRun *r = runs->first();
01422 while ( r ) {
01423 if(r->level %2) {
01424
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
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 #ifdef BIDI_DEBUG
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