00001
00023 #include "bidi.h"
00024 #include "break_lines.h"
00025 #include "render_flow.h"
00026 #include "render_text.h"
00027 using namespace khtml;
00028
00029 #include "kdebug.h"
00030 #include "qdatetime.h"
00031 #include "qfontmetrics.h"
00032
00033 #define BIDI_DEBUG 0
00034
00035
00036 #if BIDI_DEBUG > 1
00037
00038
00039 static const char *directions[] = {
00040 "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
00041 "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN"
00042 };
00043
00044 inline kdbgstream &operator<<(kdbgstream &stream, QChar::Direction d) {
00045 return (stream << directions[d]);
00046 }
00047
00048
00049 #endif
00050
00051 inline BidiIterator::BidiIterator()
00052 {
00053 par = 0;
00054 obj = 0;
00055 pos = 0;
00056 }
00057
00058 static BidiIterator sor;
00059 static BidiIterator eor;
00060 static BidiIterator last;
00061 static BidiIterator current;
00062 static BidiContext *context;
00063 static BidiStatus status;
00064 static QPtrList<BidiRun> *sruns = 0;
00065 static QChar::Direction dir;
00066 static bool adjustEmbeddding = false;
00067 static bool emptyRun = true;
00068 static int numSpaces;
00069
00070 static void embed( QChar::Direction d );
00071 static void appendRun();
00072
00073
00074
00075
00076
00077
00078
00079 BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
00080 : level(l) , override(o), dir(e)
00081 {
00082 parent = p;
00083 if(p) {
00084 p->ref();
00085 basicDir = p->basicDir;
00086 } else
00087 basicDir = e;
00088 count = 0;
00089 }
00090
00091 BidiContext::~BidiContext()
00092 {
00093 if(parent) parent->deref();
00094 }
00095
00096 void BidiContext::ref() const
00097 {
00098 count++;
00099 }
00100
00101 void BidiContext::deref() const
00102 {
00103 count--;
00104 if(count <= 0) delete this;
00105 }
00106
00107
00108
00109 inline bool operator==( const BidiIterator &it1, const BidiIterator &it2 )
00110 {
00111 if(it1.pos != it2.pos) return false;
00112 if(it1.obj != it2.obj) return false;
00113 return true;
00114 }
00115
00116 inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 )
00117 {
00118 if(it1.pos != it2.pos) return true;
00119 if(it1.obj != it2.obj) return true;
00120 return false;
00121 }
00122
00123 static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current)
00124 {
00125 RenderObject *next = 0;
00126 while(current != 0)
00127 {
00128
00129 if(!current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
00130 next = current->firstChild();
00131 if ( next && adjustEmbeddding ) {
00132 EUnicodeBidi ub = next->style()->unicodeBidi();
00133 if ( ub != UBNormal ) {
00134 EDirection dir = next->style()->direction();
00135
00136 QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
00137 : ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
00138 embed( d );
00139 }
00140 }
00141 }
00142 if(!next) {
00143 while(current && current != par) {
00144 next = current->nextSibling();
00145 if(next) break;
00146 if ( adjustEmbeddding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
00147 embed( QChar::DirPDF );
00148 }
00149 current = current->parent();
00150 }
00151 }
00152
00153 if(!next) break;
00154
00155 if(next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned())
00156 break;
00157 current = next;
00158 }
00159 return next;
00160 }
00161
00162 static RenderObject *first( RenderObject *par )
00163 {
00164 if(!par->firstChild()) return 0;
00165 RenderObject *o = par->firstChild();
00166
00167 if(!o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
00168 o = Bidinext( par, o );
00169
00170 return o;
00171 }
00172
00173 BidiIterator::BidiIterator(RenderFlow *_par)
00174 {
00175 par = _par;
00176 if ( par && adjustEmbeddding ) {
00177 EUnicodeBidi ub = par->style()->unicodeBidi();
00178 if ( ub != UBNormal ) {
00179 EDirection dir = par->style()->direction();
00180
00181 QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
00182 : ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
00183 embed( d );
00184 }
00185 }
00186 obj = first( par );
00187 pos = 0;
00188 isText = obj ? obj->isText() : false;
00189 }
00190
00191 inline BidiIterator::BidiIterator(const BidiIterator &it)
00192 {
00193 par = it.par;
00194 obj = it.obj;
00195 pos = it.pos;
00196 isText = obj ? obj->isText() : false;
00197 }
00198
00199 inline BidiIterator::BidiIterator(RenderFlow *_par, RenderObject *_obj, int _pos)
00200 {
00201 par = _par;
00202 obj = _obj;
00203 pos = _pos;
00204 isText = obj ? obj->isText() : false;
00205 }
00206
00207 inline BidiIterator &BidiIterator::operator = (const BidiIterator &it)
00208 {
00209 obj = it.obj;
00210 pos = it.pos;
00211 par = it.par;
00212 isText = obj ? obj->isText() : false;
00213 return *this;
00214 }
00215
00216 inline void BidiIterator::operator ++ ()
00217 {
00218 if(!obj) return;
00219 if(isText) {
00220 pos++;
00221 if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
00222 obj = Bidinext( par, obj );
00223 isText = obj ? obj->isText() : false;
00224 pos = 0;
00225 }
00226 } else {
00227 obj = Bidinext( par, obj );
00228 isText = obj ? obj->isText() : false;
00229 pos = 0;
00230 }
00231 }
00232
00233 inline bool BidiIterator::atEnd() const
00234 {
00235 if(!obj) return true;
00236 return false;
00237 }
00238
00239 static const QChar nbsp = QChar(0xA0);
00240
00241 inline const QChar &BidiIterator::current() const
00242 {
00243 if( !isText ) return nbsp;
00244 return static_cast<RenderText *>(obj)->text()[pos];
00245 }
00246
00247 inline QChar::Direction BidiIterator::direction() const
00248 {
00249 if( !isText ) return QChar::DirON;
00250
00251 RenderText *renderTxt = static_cast<RenderText *>( obj );
00252 if ( pos >= renderTxt->stringLength() )
00253 return QChar::DirON;
00254 return renderTxt->text()[pos].direction();
00255 }
00256
00257
00258
00259 static void appendRun()
00260 {
00261 if ( emptyRun ) return;
00262 #if BIDI_DEBUG > 1
00263 kdDebug(6041) << "appendRun: dir="<<(int)dir<<endl;
00264 #endif
00265
00266 bool b = adjustEmbeddding;
00267 adjustEmbeddding = false;
00268
00269 int start = sor.pos;
00270 RenderObject *obj = sor.obj;
00271 while( obj && obj != eor.obj ) {
00272 if(!obj->isHidden()) {
00273
00274 sruns->append( new BidiRun(start, obj->length(), obj, context, dir) );
00275 }
00276 start = 0;
00277 obj = Bidinext( sor.par, obj );
00278 }
00279 if( obj && !obj->isHidden()) {
00280
00281 sruns->append( new BidiRun(start, eor.pos + 1, obj, context, dir) );
00282 }
00283
00284 ++eor;
00285 sor = eor;
00286 dir = QChar::DirON;
00287 status.eor = QChar::DirON;
00288 adjustEmbeddding = b;
00289 }
00290
00291 static void embed( QChar::Direction d )
00292 {
00293 #if BIDI_DEBUG > 1
00294 qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun );
00295 #endif
00296 bool b = adjustEmbeddding ;
00297 adjustEmbeddding = false;
00298 if ( d == QChar::DirPDF ) {
00299 BidiContext *c = context->parent;
00300 if(c && sruns) {
00301 if ( eor != last ) {
00302 appendRun();
00303 eor = last;
00304 }
00305 appendRun();
00306 emptyRun = true;
00307 status.last = context->dir;
00308 context->deref();
00309 context = c;
00310 if(context->override)
00311 dir = context->dir;
00312 else
00313 dir = QChar::DirON;
00314 status.lastStrong = context->dir;
00315 }
00316 } else {
00317 QChar::Direction runDir;
00318 if( d == QChar::DirRLE || d == QChar::DirRLO )
00319 runDir = QChar::DirR;
00320 else
00321 runDir = QChar::DirL;
00322 bool override;
00323 if( d == QChar::DirLRO || d == QChar::DirRLO )
00324 override = true;
00325 else
00326 override = false;
00327
00328 unsigned char level = context->level;
00329 if ( runDir == QChar::DirR ) {
00330 if(level%2)
00331 level += 2;
00332 else
00333 level++;
00334 } else {
00335 if(level%2)
00336 level++;
00337 else
00338 level += 2;
00339 }
00340
00341 if(level < 61) {
00342 if ( sruns ) {
00343 if ( eor != last ) {
00344 appendRun();
00345 eor = last;
00346 }
00347 appendRun();
00348 emptyRun = true;
00349
00350 }
00351 context = new BidiContext(level, runDir, context, override);
00352 context->ref();
00353 if ( override )
00354 dir = runDir;
00355 status.last = runDir;
00356 status.lastStrong = runDir;
00357 }
00358 }
00359 adjustEmbeddding = b;
00360 }
00361
00362
00363
00364 void RenderFlow::bidiReorderLine(const BidiIterator &start, const BidiIterator &end)
00365 {
00366 if ( start == end ) {
00367 if ( start.current() == '\n' ) {
00368 m_height += lineHeight( firstLine );
00369 }
00370 return;
00371 }
00372 #if BIDI_DEBUG > 1
00373 kdDebug(6041) << "reordering Line from " << start.obj << "/" << start.pos << " to " << end.obj << "/" << end.pos << endl;
00374 #endif
00375
00376 QPtrList<BidiRun> runs;
00377 runs.setAutoDelete(true);
00378 sruns = &runs;
00379
00380
00381
00382 dir = QChar::DirON;
00383 emptyRun = true;
00384
00385 numSpaces = 0;
00386
00387 current = start;
00388 last = current;
00389 bool atEnd = false;
00390 while( 1 ) {
00391
00392 QChar::Direction dirCurrent;
00393 if(atEnd ) {
00394
00395 BidiContext *c = context;
00396 if ( current.atEnd())
00397 while ( c->parent )
00398 c = c->parent;
00399 dirCurrent = c->dir;
00400 } else {
00401 dirCurrent = current.direction();
00402 }
00403
00404 #ifndef QT_NO_UNICODETABLES
00405
00406 if ( context->override &&
00407 dirCurrent != QChar::DirRLE &&
00408 dirCurrent != QChar::DirLRE &&
00409 dirCurrent != QChar::DirRLO &&
00410 dirCurrent != QChar::DirLRO &&
00411 dirCurrent != QChar::DirPDF ) {
00412 eor = current;
00413 goto skipbidi;
00414 }
00415
00416 #if BIDI_DEBUG > 1
00417 kdDebug(6041) << "directions: dir=" << dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << (int)context->dir << " level =" << (int)context->level << endl;
00418 #endif
00419 switch(dirCurrent) {
00420
00421
00422 case QChar::DirRLE:
00423 case QChar::DirLRE:
00424 case QChar::DirRLO:
00425 case QChar::DirLRO:
00426 case QChar::DirPDF:
00427 eor = last;
00428 embed( dirCurrent );
00429 break;
00430
00431
00432 case QChar::DirL:
00433 if(dir == QChar::DirON)
00434 dir = QChar::DirL;
00435 switch(status.last)
00436 {
00437 case QChar::DirL:
00438 eor = current; status.eor = QChar::DirL; break;
00439 case QChar::DirR:
00440 case QChar::DirAL:
00441 case QChar::DirEN:
00442 case QChar::DirAN:
00443 appendRun();
00444 break;
00445 case QChar::DirES:
00446 case QChar::DirET:
00447 case QChar::DirCS:
00448 case QChar::DirBN:
00449 case QChar::DirB:
00450 case QChar::DirS:
00451 case QChar::DirWS:
00452 case QChar::DirON:
00453 if(dir != QChar::DirL) {
00454
00455 if( context->dir == QChar::DirR ) {
00456 if(!(status.eor == QChar::DirR)) {
00457
00458 appendRun();
00459 dir = QChar::DirR;
00460 }
00461 else
00462 eor = last;
00463 appendRun();
00464 dir = QChar::DirL;
00465 status.eor = QChar::DirL;
00466 } else {
00467 if(status.eor == QChar::DirR) {
00468 appendRun();
00469 dir = QChar::DirL;
00470 } else {
00471 eor = current; status.eor = QChar::DirL; break;
00472 }
00473 }
00474 } else {
00475 eor = current; status.eor = QChar::DirL;
00476 }
00477 default:
00478 break;
00479 }
00480 status.lastStrong = QChar::DirL;
00481 break;
00482 case QChar::DirAL:
00483 case QChar::DirR:
00484 if(dir == QChar::DirON) dir = QChar::DirR;
00485 switch(status.last)
00486 {
00487 case QChar::DirR:
00488 case QChar::DirAL:
00489 eor = current; status.eor = QChar::DirR; break;
00490 case QChar::DirL:
00491 case QChar::DirEN:
00492 case QChar::DirAN:
00493 appendRun();
00494 dir = QChar::DirR;
00495 eor = current;
00496 status.eor = QChar::DirR;
00497 break;
00498 case QChar::DirES:
00499 case QChar::DirET:
00500 case QChar::DirCS:
00501 case QChar::DirBN:
00502 case QChar::DirB:
00503 case QChar::DirS:
00504 case QChar::DirWS:
00505 case QChar::DirON:
00506 if( !(status.eor == QChar::DirR) && !(status.eor == QChar::DirAL) ) {
00507
00508 if(context->dir == QChar::DirR || status.lastStrong == QChar::DirR) {
00509 appendRun();
00510 dir = QChar::DirR;
00511 eor = current;
00512 status.eor = QChar::DirR;
00513 } else {
00514 eor = last;
00515 appendRun();
00516 dir = QChar::DirR;
00517 status.eor = QChar::DirR;
00518 }
00519 } else {
00520 eor = current; status.eor = QChar::DirR;
00521 }
00522 default:
00523 break;
00524 }
00525 status.lastStrong = dirCurrent;
00526 break;
00527
00528
00529
00530 case QChar::DirNSM:
00531
00532 break;
00533 case QChar::DirEN:
00534 if(!(status.lastStrong == QChar::DirAL)) {
00535
00536 if(dir == QChar::DirON) {
00537 if(status.lastStrong == QChar::DirAL)
00538 dir = QChar::DirAN;
00539 else
00540 dir = QChar::DirL;
00541 }
00542 switch(status.last)
00543 {
00544 case QChar::DirET:
00545 if ( status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL ) {
00546 appendRun();
00547 dir = QChar::DirEN;
00548 status.eor = QChar::DirEN;
00549 }
00550
00551 case QChar::DirEN:
00552 case QChar::DirL:
00553 eor = current;
00554 status.eor = dirCurrent;
00555 break;
00556 case QChar::DirR:
00557 case QChar::DirAL:
00558 case QChar::DirAN:
00559 appendRun();
00560 status.eor = QChar::DirEN;
00561 dir = QChar::DirEN;
00562 break;
00563 case QChar::DirES:
00564 case QChar::DirCS:
00565 if(status.eor == QChar::DirEN) {
00566 eor = current; break;
00567 }
00568 case QChar::DirBN:
00569 case QChar::DirB:
00570 case QChar::DirS:
00571 case QChar::DirWS:
00572 case QChar::DirON:
00573 if(status.eor == QChar::DirR) {
00574
00575 eor = last;
00576 appendRun();
00577 dir = QChar::DirEN;
00578 status.eor = QChar::DirEN;
00579 }
00580 else if( status.eor == QChar::DirL ||
00581 (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
00582 eor = current; status.eor = dirCurrent;
00583 } else {
00584
00585 if(dir != QChar::DirL) {
00586 appendRun();
00587 eor = last;
00588 dir = QChar::DirR;
00589 appendRun();
00590 dir = QChar::DirEN;
00591 status.eor = QChar::DirEN;
00592 } else {
00593 eor = current; status.eor = dirCurrent;
00594 }
00595 }
00596 default:
00597 break;
00598 }
00599 break;
00600 }
00601 case QChar::DirAN:
00602 dirCurrent = QChar::DirAN;
00603 if(dir == QChar::DirON) dir = QChar::DirAN;
00604 switch(status.last)
00605 {
00606 case QChar::DirL:
00607 case QChar::DirAN:
00608 eor = current; status.eor = QChar::DirAN; break;
00609 case QChar::DirR:
00610 case QChar::DirAL:
00611 case QChar::DirEN:
00612 appendRun();
00613 dir = QChar::DirAN; status.eor = QChar::DirAN;
00614 break;
00615 case QChar::DirCS:
00616 if(status.eor == QChar::DirAN) {
00617 eor = current; status.eor = QChar::DirR; break;
00618 }
00619 case QChar::DirES:
00620 case QChar::DirET:
00621 case QChar::DirBN:
00622 case QChar::DirB:
00623 case QChar::DirS:
00624 case QChar::DirWS:
00625 case QChar::DirON:
00626 if(status.eor == QChar::DirR) {
00627
00628 eor = last;
00629 appendRun();
00630 dir = QChar::DirAN;
00631 status.eor = QChar::DirAN;
00632 } else if( status.eor == QChar::DirL ||
00633 (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
00634 eor = current; status.eor = dirCurrent;
00635 } else {
00636
00637 if(dir != QChar::DirL) {
00638 appendRun();
00639 eor = last;
00640 dir = QChar::DirR;
00641 appendRun();
00642 dir = QChar::DirAN;
00643 status.eor = QChar::DirAN;
00644 } else {
00645 eor = current; status.eor = dirCurrent;
00646 }
00647 }
00648 default:
00649 break;
00650 }
00651 break;
00652 case QChar::DirES:
00653 case QChar::DirCS:
00654 break;
00655 case QChar::DirET:
00656 if(status.last == QChar::DirEN) {
00657 dirCurrent = QChar::DirEN;
00658 eor = current; status.eor = dirCurrent;
00659 break;
00660 }
00661 break;
00662
00663
00664 case QChar::DirBN:
00665 break;
00666
00667 case QChar::DirB:
00668
00669 break;
00670 case QChar::DirS:
00671
00672 break;
00673 case QChar::DirWS:
00674 numSpaces++;
00675 case QChar::DirON:
00676 break;
00677 default:
00678 break;
00679 }
00680
00681 skipbidi:
00682
00683
00684 if(current.atEnd()) break;
00685
00686
00687 switch(dirCurrent)
00688 {
00689 case QChar::DirET:
00690 case QChar::DirES:
00691 case QChar::DirCS:
00692 case QChar::DirS:
00693 case QChar::DirWS:
00694 case QChar::DirON:
00695 switch(status.last)
00696 {
00697 case QChar::DirL:
00698 case QChar::DirR:
00699 case QChar::DirAL:
00700 case QChar::DirEN:
00701 case QChar::DirAN:
00702 status.last = dirCurrent;
00703 break;
00704 default:
00705 status.last = QChar::DirON;
00706 }
00707 break;
00708 case QChar::DirNSM:
00709 case QChar::DirBN:
00710
00711 break;
00712 case QChar::DirEN:
00713 if ( status.last == QChar::DirL ) {
00714 status.last = QChar::DirL;
00715 break;
00716 }
00717
00718 default:
00719 status.last = dirCurrent;
00720 }
00721 #endif
00722
00723 if ( atEnd ) break;
00724 last = current;
00725
00726 if ( emptyRun ) {
00727 sor = current;
00728 eor = current;
00729 emptyRun = false;
00730 }
00731
00732
00733
00734 adjustEmbeddding = true;
00735 ++current;
00736 adjustEmbeddding = false;
00737
00738 if ( current == end ) {
00739 if ( emptyRun )
00740 break;
00741 atEnd = true;
00742 }
00743 }
00744
00745 #if BIDI_DEBUG > 0
00746 kdDebug(6041) << "reached end of line current=" << current.obj << "/" << current.pos
00747 << ", eor=" << eor.obj << "/" << eor.pos << endl;
00748 #endif
00749 if ( !emptyRun && sor != current ) {
00750 eor = last;
00751 appendRun();
00752 }
00753
00754 BidiContext *endEmbed = context;
00755
00756
00757
00758
00759
00760
00761
00762 uchar levelLow = 128;
00763 uchar levelHigh = 0;
00764 BidiRun *r = runs.first();
00765
00766 while ( r ) {
00767
00768 if ( r->level > levelHigh )
00769 levelHigh = r->level;
00770 if ( r->level < levelLow )
00771 levelLow = r->level;
00772 r = runs.next();
00773 }
00774
00775
00776
00777
00778
00779
00780 if( !(levelLow%2) ) levelLow++;
00781
00782 #if BIDI_DEBUG > 0
00783 kdDebug(6041) << "lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
00784 kdDebug(6041) << "logical order is:" << endl;
00785 QPtrListIterator<BidiRun> it2(runs);
00786 BidiRun *r2;
00787 for ( ; (r2 = it2.current()); ++it2 )
00788 kdDebug(6041) << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
00789 #endif
00790
00791 int count = runs.count() - 1;
00792
00793
00794 if(!style()->visuallyOrdered()) {
00795 while(levelHigh >= levelLow) {
00796 int i = 0;
00797 while ( i < count ) {
00798 while(i < count && runs.at(i)->level < levelHigh)
00799 i++;
00800 int start = i;
00801 while(i <= count && runs.at(i)->level >= levelHigh)
00802 i++;
00803 int end = i-1;
00804
00805 if(start != end) {
00806
00807 for(int j = 0; j < (end-start+1)/2; j++)
00808 {
00809 BidiRun *first = runs.take(start+j);
00810 BidiRun *last = runs.take(end-j-1);
00811 runs.insert(start+j, last);
00812 runs.insert(end-j, first);
00813 }
00814 }
00815 i++;
00816 if(i >= count) break;
00817 }
00818 levelHigh--;
00819 }
00820 }
00821
00822 #if BIDI_DEBUG > 0
00823 kdDebug(6041) << "visual order is:" << endl;
00824 QPtrListIterator<BidiRun> it3(runs);
00825 BidiRun *r3;
00826 for ( ; (r3 = it3.current()); ++it3 )
00827 {
00828 kdDebug(6041) << " " << r3 << endl;
00829 }
00830 #endif
00831
00832 int maxPositionTop = 0;
00833 int maxPositionBottom = 0;
00834 int maxAscent = 0;
00835 int maxDescent = 0;
00836 r = runs.first();
00837 while ( r ) {
00838 r->height = r->obj->lineHeight( firstLine );
00839 r->baseline = r->obj->baselinePosition( firstLine );
00840
00841
00842 r->vertical = r->obj->verticalPositionHint( firstLine );
00843
00844
00845 if ( r->vertical == PositionTop ) {
00846 if ( maxPositionTop < r->height ) maxPositionTop = r->height;
00847 }
00848 else if ( r->vertical == PositionBottom ) {
00849 if ( maxPositionBottom < r->height ) maxPositionBottom = r->height;
00850 }
00851 else {
00852 int ascent = r->baseline - r->vertical;
00853 int descent = r->height - ascent;
00854 if(maxAscent < ascent) maxAscent = ascent;
00855 if(maxDescent < descent) maxDescent = descent;
00856 }
00857 r = runs.next();
00858 }
00859 if ( maxAscent+maxDescent < QMAX( maxPositionTop, maxPositionBottom ) ) {
00860
00861
00862
00863
00864 for ( r = runs.first(); r; r = runs.next() ) {
00865 if ( r->vertical == PositionTop ) {
00866 if ( maxAscent + maxDescent < r->height )
00867 maxDescent = r->height - maxAscent;
00868 }
00869 else if ( r->vertical == PositionBottom ) {
00870 if ( maxAscent + maxDescent < r->height )
00871 maxAscent = r->height - maxDescent;
00872 }
00873 else
00874 continue;
00875
00876 if ( maxAscent + maxDescent >= QMAX( maxPositionTop, maxPositionBottom ) )
00877 break;
00878
00879 }
00880 }
00881 int maxHeight = maxAscent + maxDescent;
00882
00883
00884 r = runs.first();
00885
00886
00887
00888
00889
00890
00891
00892 int totWidth = 0;
00893 #if BIDI_DEBUG > 0
00894 kdDebug( 6040 ) << "starting run.." << endl;
00895 #endif
00896 while ( r ) {
00897 if(r->vertical == PositionTop)
00898 r->vertical = m_height;
00899 else if(r->vertical == PositionBottom)
00900 r->vertical = m_height + maxHeight - r->height;
00901 else
00902 r->vertical += m_height + maxAscent - r->baseline;
00903
00904 if(r->obj->isText())
00905 r->width = static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, firstLine);
00906 else {
00907 r->obj->calcWidth();
00908 r->width = r->obj->width()+r->obj->marginLeft()+r->obj->marginRight();
00909 }
00910 #if BIDI_DEBUG > 0
00911 kdDebug(6040) << "object="<< r->obj << " placing at vertical=" << r->vertical <<" width=" << r->width <<endl;
00912 #endif
00913 totWidth += r->width;
00914 r = runs.next();
00915 }
00916
00917
00918
00919
00920 r = runs.first();
00921 int x = leftOffset(m_height);
00922 int availableWidth = lineWidth(m_height);
00923 switch(style()->textAlign()) {
00924 case LEFT:
00925 numSpaces = 0;
00926 break;
00927 case JUSTIFY:
00928 if(numSpaces != 0 && !current.atEnd() && !current.obj->isBR() )
00929 break;
00930
00931 case TAAUTO:
00932 numSpaces = 0;
00933
00934 if ( endEmbed->basicDir == QChar::DirL )
00935 break;
00936 case RIGHT:
00937 x += availableWidth - totWidth;
00938 numSpaces = 0;
00939 break;
00940 case CENTER:
00941 case KONQ_CENTER:
00942 int xd = (availableWidth - totWidth)/2;
00943 x += xd>0?xd:0;
00944 numSpaces = 0;
00945 break;
00946 }
00947 while ( r ) {
00948 #if BIDI_DEBUG > 1
00949 kdDebug(6040) << "positioning " << r->obj << " start=" << r->start << " stop=" << r->stop << " x=" << x << " width=" << r->width << " yPos=" << r->vertical << endl;
00950 #endif
00951 int spaceAdd = 0;
00952 if ( numSpaces > 0 ) {
00953 if ( r->obj->isText() ) {
00954
00955 int spaces = 0;
00956 for ( int i = r->start; i < r->stop; i++ )
00957 if ( static_cast<RenderText *>(r->obj)->text()[i].direction() == QChar::DirWS )
00958 spaces++;
00959 if ( spaces > numSpaces )
00960 spaces = numSpaces;
00961 spaceAdd = (availableWidth - totWidth)*spaces/numSpaces;
00962 numSpaces -= spaces;
00963 totWidth += spaceAdd;
00964 }
00965 }
00966 r->obj->position(x, r->vertical, r->start, r->stop - r->start, r->width, r->level%2, firstLine, spaceAdd);
00967 x += r->width + spaceAdd;
00968 r = runs.next();
00969 }
00970
00971 m_height += maxHeight;
00972
00973 sruns = 0;
00974 }
00975
00976
00977 void RenderFlow::layoutInlineChildren( bool relayoutChildren )
00978 {
00979 invalidateVerticalPositions();
00980 #ifdef DEBUG_LAYOUT
00981 QTime qt;
00982 qt.start();
00983 kdDebug( 6040 ) << renderName() << " layoutInlineChildren( " << this <<" )" << endl;
00984 #endif
00985 #if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
00986 kdDebug(6041) << " ------- bidi start " << this << " -------" << endl;
00987 #endif
00988 int toAdd = style()->borderBottomWidth();
00989 m_height = style()->borderTopWidth();
00990
00991 emptyRun = true;
00992
00993 m_height += paddingTop();
00994 toAdd += paddingBottom();
00995
00996 if(firstChild()) {
00997
00998 RenderObject *o = first( this );
00999 while ( o ) {
01000 if(o->isReplaced() || o->isFloating() || o->isPositioned()) {
01001
01002 if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
01003 o->setLayouted(false);
01004 if( !o->layouted() )
01005 o->layout();
01006 if(o->isPositioned())
01007 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
01008 }
01009 else if(o->isText())
01010 static_cast<RenderText *>(o)->deleteSlaves();
01011 o = Bidinext( this, o );
01012 }
01013
01014 BidiContext *startEmbed;
01015 status = BidiStatus();
01016 if( style()->direction() == LTR ) {
01017 startEmbed = new BidiContext( 0, QChar::DirL );
01018 status.eor = QChar::DirL;
01019 } else {
01020 startEmbed = new BidiContext( 1, QChar::DirR );
01021 status.eor = QChar::DirR;
01022 }
01023 startEmbed->ref();
01024
01025 context = startEmbed;
01026 adjustEmbeddding = true;
01027 BidiIterator start(this);
01028 adjustEmbeddding = false;
01029 BidiIterator end(this);
01030
01031 firstLine = true;
01032 while( !end.atEnd() ) {
01033 start = end;
01034
01035 end = findNextLineBreak(start);
01036 if( start.atEnd() ) break;
01037 bidiReorderLine(start, end);
01038
01039 if( end == start || (end.obj && end.obj->isBR() && !start.obj->isBR() ) ) {
01040 adjustEmbeddding = true;
01041 ++end;
01042 adjustEmbeddding = false;
01043 } else if(m_pre && end.current() == QChar('\n') ) {
01044 adjustEmbeddding = true;
01045 ++end;
01046 adjustEmbeddding = false;
01047 }
01048
01049 newLine();
01050 firstLine = false;
01051 }
01052
01053
01054 while ( context ) {
01055 BidiContext *parent = context->parent;
01056 delete context;
01057 context = parent;
01058 }
01059 }
01060 m_height += toAdd;
01061
01062
01063 positionNewFloats();
01064
01065 #if BIDI_DEBUG > 1
01066 kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
01067 #endif
01068
01069
01070 }
01071
01072 BidiIterator RenderFlow::findNextLineBreak(BidiIterator &start)
01073 {
01074 int width = lineWidth(m_height);
01075 int w = 0;
01076 int tmpW = 0;
01077 #ifdef DEBUG_LINEBREAKS
01078 kdDebug(6041) << "RenderFlow::findNextLineBreak: " << this << endl;
01079 kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
01080 kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
01081 #endif
01082
01083
01084
01085 if(!m_pre) {
01086
01087 while(!start.atEnd() &&
01088 #ifndef QT_NO_UNICODETABLES
01089 ( start.direction() == QChar::DirWS || start.obj->isSpecial() )
01090 #else
01091 ( start.current() == ' ' || start.obj->isSpecial() )
01092 #endif
01093 ) {
01094 if( start.obj->isSpecial() ) {
01095 RenderObject *o = start.obj;
01096
01097 if(o->isFloating()) {
01098 insertSpecialObject(o);
01099
01100
01101
01102 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
01103 positionNewFloats();
01104 width = lineWidth(m_height);
01105 }
01106 } else if(o->isPositioned()) {
01107 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
01108 }
01109 }
01110
01111 adjustEmbeddding = true;
01112 ++start;
01113 adjustEmbeddding = false;
01114 }
01115 }
01116 if ( start.atEnd() )
01117 return start;
01118
01119 BidiIterator lBreak = start;
01120
01121 RenderObject *o = start.obj;
01122 RenderObject *last = o;
01123 int pos = start.pos;
01124
01125 while( o ) {
01126 #ifdef DEBUG_LINEBREAKS
01127 kdDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW << endl;
01128 #endif
01129 if(o->isBR()) {
01130 if( w + tmpW <= width ) {
01131 lBreak = o;
01132
01133 m_clearStatus = (EClear) (m_clearStatus | o->style()->clear());
01134 }
01135 goto end;
01136 } else if(o->isFloating()) {
01137 insertSpecialObject(o);
01138
01139
01140
01141 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
01142 positionNewFloats();
01143 width = lineWidth(m_height);
01144 }
01145 } else if(o->isPositioned()) {
01146 static_cast<RenderFlow*>(o->containingBlock())->insertSpecialObject(o);
01147 } else if ( o->isText() ) {
01148 RenderText *t = static_cast<RenderText *>(o);
01149 int strlen = t->stringLength();
01150 int len = strlen - pos;
01151 QChar *str = t->text();
01152 if (style()->whiteSpace() == NOWRAP || t->style()->whiteSpace() == NOWRAP) {
01153 tmpW += t->maxWidth();
01154 pos = len;
01155 len = 0;
01156 } else {
01157 const Font *f = t->htmlFont( firstLine );
01158
01159 int lastSpace = pos;
01160 bool isPre = style()->whiteSpace() == PRE;
01161 while(len) {
01162 if( (isPre && str[pos] == '\n') ||
01163 (!isPre && isBreakable( str, pos, strlen ) ) ) {
01164 tmpW += t->width(lastSpace, pos - lastSpace, f);
01165 #ifdef DEBUG_LINEBREAKS
01166 kdDebug(6041) << "found space: '" << QString( str, pos ).latin1() << "' +" << tmpW << " -> w = " << w << endl;
01167 #endif
01168 if ( !isPre && w + tmpW > width && w == 0 ) {
01169 int fb = floatBottom();
01170 int newLineWidth = lineWidth(fb);
01171 if(!w && m_height < fb && width < newLineWidth) {
01172 m_height = fb;
01173 width = newLineWidth;
01174 #ifdef DEBUG_LINEBREAKS
01175 kdDebug() << "RenderFlow::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
01176 #endif
01177 }
01178 }
01179 if ( !isPre && w + tmpW > width )
01180 goto end;
01181
01182 lBreak.obj = o;
01183 lBreak.pos = pos;
01184
01185 if( str[pos] == '\n' ) {
01186 #ifdef DEBUG_LINEBREAKS
01187 kdDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl;
01188 #endif
01189 return lBreak;
01190 }
01191 w += tmpW;
01192 tmpW = 0;
01193 lastSpace = pos;
01194 }
01195 pos++;
01196 len--;
01197 }
01198
01199 tmpW += t->width(lastSpace, pos - lastSpace, f);
01200 if (!isPre && w + tmpW < width && pos && str[pos-1] != nbsp)
01201 lBreak = Bidinext( start.par, o );
01202 }
01203 } else if ( o->isReplaced() ) {
01204 tmpW += o->width()+o->marginLeft()+o->marginRight();
01205
01206 if ( w + tmpW > width )
01207 goto end;
01208
01209 if (o->style()->whiteSpace() != NOWRAP) {
01210 w += tmpW;
01211 tmpW = 0;
01212 lBreak = o;
01213 lBreak.pos = pos;
01214 }
01215 } else
01216 KHTMLAssert( false );
01217
01218 if( w + tmpW > width+1 && style()->whiteSpace() == NORMAL ) {
01219
01220
01221
01222 int fb = floatBottom();
01223 int newLineWidth = lineWidth(fb);
01224 if( !w && m_height < fb && width < newLineWidth ) {
01225 m_height = fb;
01226 width = newLineWidth;
01227 #ifdef DEBUG_LINEBREAKS
01228 kdDebug() << "RenderFlow::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
01229 #endif
01230 }
01231 if( !w && w + tmpW > width+1 && (o != start.obj || (unsigned) pos != start.pos) ) {
01232
01233
01234 lBreak = o;
01235 if (o == last)
01236 lBreak.pos = pos;
01237 if (unsigned ( pos ) >= o->length())
01238 lBreak = Bidinext(start.par, o);
01239 }
01240 goto end;
01241 }
01242
01243 last = o;
01244 o = Bidinext( start.par, o );
01245 pos = 0;
01246 }
01247
01248 #ifdef DEBUG_LINEBREAKS
01249 kdDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW << endl;
01250 #endif
01251 if( w + tmpW <= width )
01252 lBreak = 0;
01253
01254 end:
01255
01256 if( lBreak == start && !lBreak.obj->isBR() ) {
01257
01258 if ( m_pre )
01259 lBreak = pos ? Bidinext(start.par, o) : o;
01260 else {
01261 if( last != o ) {
01262
01263 lBreak = o;
01264 } else {
01265 int w = 0;
01266 if( lBreak.obj->isText() )
01267 w += static_cast<RenderText *>(lBreak.obj)->width(lBreak.pos, 1);
01268 else
01269 w += lBreak.obj->width();
01270 while( lBreak.obj && w < width ) {
01271 ++lBreak;
01272 if( !lBreak.obj ) break;
01273 if( lBreak.obj->isText() )
01274 w += static_cast<RenderText *>(lBreak.obj)->width(lBreak.pos, 1);
01275 else
01276 w += lBreak.obj->width();
01277 }
01278 }
01279 }
01280 }
01281
01282
01283 if( lBreak == start )
01284 ++lBreak;
01285
01286 #ifdef DEBUG_LINEBREAKS
01287 kdDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << "/" << width << endl;
01288 #endif
01289 return lBreak;
01290 }
01291
01292
01293 #undef BIDI_DEBUG
01294 #undef DEBUG_LINEBREAKS
01295 #undef DEBUG_LAYOUT