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
#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
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
#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
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
#if (BIDI_DEBUG >= 1)
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
#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
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 )
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
#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