00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <qglobal.h>
00021 #if QT_VERSION >= 0x030200
00022 #define INDIC
00023 #endif
00024
00025 #include "kotextformatter.h"
00026 #include "kotextformat.h"
00027 #include "kotextdocument.h"
00028 #include "kozoomhandler.h"
00029 #include "kohyphen/kohyphen.h"
00030 #include "koparagcounter.h"
00031
00032 #include <kdebug.h>
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00046
00047
00048 KoTextFormatter::KoTextFormatter()
00049 {
00050 try {
00051 m_hyphenator = KoHyphenator::self();
00052 } catch ( KoHyphenatorException& e )
00053 {
00054 m_hyphenator = 0L;
00055 }
00056 }
00057
00058 KoTextFormatter::~KoTextFormatter()
00059 {
00060 }
00061
00062
00063
00064 struct TemporaryWordData
00065 {
00066 int baseLine;
00067 int height;
00068 int lineWidth;
00069 };
00070
00071 bool KoTextFormatter::format( KoTextDocument *doc, KoTextParag *parag,
00072 int start, const QMap<int, KoTextParagLineStart*> &,
00073 int& y, int& widthUsed )
00074 {
00075 KoTextFormatterCore formatter( this, doc, parag, start );
00076 bool worked = formatter.format();
00077 y = formatter.resultY();
00078 widthUsed = formatter.widthUsed();
00079 return worked;
00080 }
00081
00082 KoTextFormatterCore::KoTextFormatterCore( KoTextFormatter* _settings,
00083 KoTextDocument *_doc, KoTextParag *_parag,
00084 int _start )
00085 : settings(_settings), doc(_doc), parag(_parag), start(_start)
00086 {
00087 }
00088
00089 QPair<int, int> KoTextFormatterCore::determineCharWidth()
00090 {
00091 int ww, pixelww;
00092 KoZoomHandler *zh = doc->formattingZoomHandler();
00093 if ( c->c != '\t' || c->isCustom() ) {
00094 KoTextFormat *charFormat = c->format();
00095 if ( c->isCustom() ) {
00096 ww = c->customItem()->width;
00097 Q_ASSERT( ww >= 0 );
00098 ww = QMAX(0, ww);
00099 #ifndef REF_IS_LU
00100 pixelww = zh->layoutUnitToPixelX( ww );
00101 #endif
00102 } else {
00103 ww = charFormat->charWidthLU( c, parag, i );
00104 #ifndef REF_IS_LU
00105
00106 pixelww = charFormat->charWidth( zh, true, c, parag, i );
00107 #endif
00108 }
00109 } else {
00110 int nx = parag->nextTab( i, x );
00111 if ( nx < x )
00112 ww = availableWidth - x;
00113 else
00114 ww = nx - x + 1;
00115 #ifndef REF_IS_LU
00116 pixelww = zh->layoutUnitToPixelX( ww );
00117 #endif
00118 }
00119 Q_ASSERT( ww >= 0 );
00120 c->width = ww;
00121 return qMakePair(ww, pixelww);
00122 }
00123
00124
00125 int KoTextFormatterCore::leftMargin( bool firstLine ) const
00126 {
00127 int left = parag->leftMargin() + doc->leftMargin() ;
00128 if ( firstLine && !parag->string()->isRightToLeft() )
00129 {
00130 left += parag->firstLineMargin();
00131
00132 if( parag->counter() &&
00133 ( parag->counter()->alignment() == Qt::AlignLeft ||
00134 parag->counter()->alignment() == Qt::AlignAuto ) )
00135 left += parag->counterWidth();
00136 }
00137 return left;
00138 }
00139
00140 int KoTextFormatterCore::rightMargin( bool firstLine ) const
00141 {
00142 int right = parag->rightMargin();
00143 if ( firstLine && parag->string()->isRightToLeft() )
00144 right += parag->firstLineMargin();
00145 return right;
00146 }
00147
00148 bool KoTextFormatterCore::format()
00149 {
00150 start = 0;
00151 KoTextString *string = parag->string();
00152 if ( start == 0 )
00153 c = &string->at( start );
00154 else
00155 c = 0;
00156
00157 KoTextStringChar *firstChar = 0;
00158 int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
00159 int initialLMargin = leftMargin( true );
00160
00161 y = doc && doc->addMargins() ? parag->topMargin() : 0;
00162
00163
00164
00165 if ( !parag->prev() )
00166 y = 0;
00167 else if ( parag->breakableTopMargin() )
00168 {
00169 int shift = doc->flow()->adjustFlow( parag->rect().y(),
00170 0 ,
00171 parag->breakableTopMargin() );
00172 if ( shift > 0 )
00173 {
00174
00175
00176 y = shift;
00177 }
00178
00179 }
00180
00181 y += parag->topMargin() - parag->breakableTopMargin();
00182 int len = parag->length();
00183
00184 int initialHeight = c->height();
00185
00186 int currentRightMargin = rightMargin( true );
00187 int initialRMargin = currentRightMargin;
00188
00189 i = start;
00190 parag->tabCache().clear();
00191 x = 0;
00192
00193
00194
00195
00196
00197 QPair<int, int> widths = determineCharWidth();
00198 int ww = widths.first;
00199 #ifndef REF_IS_LU
00200 int pixelww = widths.second;
00201 #endif
00202
00203
00204
00205 int dw = 0;
00206
00207 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00208 ww, initialLMargin, initialRMargin, dw,
00209 parag );
00210
00211
00212 x = initialLMargin;
00213
00214 int maxY = doc ? doc->flow()->availableHeight() : -1;
00215
00216 availableWidth = dw - initialRMargin;
00217 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH)
00218 kdDebug(32500) << "KoTextFormatter::format formatting parag " << parag->paragId()
00219 << " text:" << parag->string()->toString() << "\n"
00220 << " left=" << left << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " availableWidth=" << availableWidth << " maxY=" << maxY << endl;
00221 #else
00222 if ( availableWidth == 0 )
00223 kdDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, availableWidth=0" << endl;
00224 if ( maxY == 0 )
00225 kdDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, maxY=0" << endl;
00226 #endif
00227 bool fullWidth = TRUE;
00228
00229
00230
00231
00232
00233
00234
00235 wused = 0;
00236
00237 bool wrapEnabled = settings->isWrapEnabled( parag );
00238 QValueList<TemporaryWordData> tempWordData;
00239
00240 #ifdef DEBUG_FORMATTER
00241 kdDebug(32500) << "Initial KoTextParagLineStart at y=" << y << endl;
00242 #endif
00243 KoTextParagLineStart *lineStart = new KoTextParagLineStart( y, 0, 0 );
00244 parag->insertLineStart( 0, lineStart );
00245 int lastBreak = -1;
00246
00247
00248 int tmpBaseLine = 0, tmph = 0;
00249
00250 int tmpWused = 0;
00251 bool lastWasNonInlineCustom = FALSE;
00252 bool abort = false;
00253
00254 int align = parag->alignment();
00255 if ( align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto )
00256 align = doc->alignment();
00257
00258 int col = 0;
00259
00260 maxAvailableWidth = qMakePair( 0, 0 );
00261
00262 KoZoomHandler *zh = doc->formattingZoomHandler();
00263 int pixelx = zh->layoutUnitToPixelX( x );
00264 int lastPixelx = 0;
00265
00266 KoTextStringChar* lastChr = 0;
00267 for ( ; i < len; ++i, ++col ) {
00268 if ( c )
00269 lastChr = c;
00270 c = &string->at( i );
00271 if ( i > 0 && (x > initialLMargin || ww == 0) || lastWasNonInlineCustom ) {
00272 c->lineStart = 0;
00273 } else {
00274 c->lineStart = 1;
00275 firstChar = c;
00276 tmph = c->height();
00277 tmpBaseLine = c->ascent();
00278 #ifdef DEBUG_FORMATTER_VERT
00279 kdDebug(32500) << "New line, initializing tmpBaseLine=" << tmpBaseLine << " tmph=" << tmph << endl;
00280 #endif
00281 }
00282
00283 if ( c->isCustom() && c->customItem()->placement() != KoTextCustomItem::PlaceInline )
00284 lastWasNonInlineCustom = TRUE;
00285 else
00286 lastWasNonInlineCustom = FALSE;
00287
00288 QPair<int, int> widths = determineCharWidth();
00289 ww = widths.first;
00290 pixelww = widths.second;
00291
00292
00293
00294
00295 if ( abort ) {
00296 x += ww;
00297 c->x = x;
00298 continue;
00299 }
00300
00301
00302 if ( c->isCustom() && c->customItem()->ownLine() ) {
00303 #ifdef DEBUG_FORMATTER
00304 kdDebug(32500) << "i=" << i << "/" << len << " custom item with ownline" << endl;
00305 #endif
00306 int rightMargin = currentRightMargin;
00307 x = left;
00308 if ( doc )
00309 doc->flow()->adjustMargins( y + parag->rect().y(), parag->rect().height(), 15,
00310 x, rightMargin, dw, parag );
00311 int w = dw - rightMargin;
00312 c->customItem()->resize( w - x );
00313 y += lineStart->h;
00314 lineStart = new KoTextParagLineStart( y, c->ascent(), c->height() );
00315
00316 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00317 lineStart->h += lineStart->lineSpacing;
00318 lineStart->w = dw;
00319 parag->insertLineStart( i, lineStart );
00320 tempWordData.clear();
00321 c->lineStart = 1;
00322 firstChar = c;
00323 x = 0xffffff;
00324
00325 continue;
00326 }
00327
00328 #ifdef DEBUG_FORMATTER
00329 kdDebug(32500) << "c='" << QString(c->c) << "' i=" << i << "/" << len << " x=" << x << " ww=" << ww << " availableWidth=" << availableWidth << " (test is x+ww>aW) lastBreak=" << lastBreak << " isBreakable=" << settings->isBreakable(string, i) << endl;
00330 #endif
00331
00332 if ( wrapEnabled
00333
00334 && ( x + ww > availableWidth &&
00335 ( lastBreak != -1 || settings->allowBreakInWords() )
00336 )
00337
00338
00339 && ( !settings->isBreakable( string, i ) ||
00340 ( i > 1 && lastBreak == i-1 && settings->isBreakable( string, i-2 ) ) ||
00341 lastBreak == -2 )
00342
00343
00344
00345
00346
00347
00349
00350
00351 || lastChr->c == '\n' && parag->isNewLinesAllowed() && lastBreak > -1 )
00352 {
00353 #ifdef DEBUG_FORMATTER
00354 kdDebug(32500) << "BREAKING" << endl;
00355 #endif
00356
00357
00358
00359 bool hyphenated = false;
00360
00361 if ( settings->hyphenator() )
00362 {
00363 int wordStart = QMAX(0, lastBreak+1);
00364
00365 int maxlen = i - wordStart;
00366 QString word = string->mid( wordStart, maxlen );
00367 int wordEnd = i;
00368
00369 while ( wordEnd < len && !settings->isBreakable( string, wordEnd ) ) {
00370 word += string->at(wordEnd).c;
00371 wordEnd++;
00372 }
00373 if ( word.length() > 1 )
00374 {
00375 QString lang = string->at(wordStart).format()->language();
00376 char * hyphens = settings->hyphenator()->hyphens( word, lang );
00377 #if defined(DEBUG_FORMATTER) || defined(DEBUG_HYPHENATION)
00378 kdDebug(32500) << "Hyphenation: word=" << word << " lang=" << lang << " hyphens=" << hyphens << " maxlen=" << maxlen << endl;
00379 kdDebug(32500) << "Parag indexes: wordStart=" << wordStart << " lastBreak=" << lastBreak << " i=" << i << endl;
00380 #endif
00381 int hylen = strlen(hyphens);
00382 Q_ASSERT( maxlen <= hylen );
00383
00384
00385 int minPos = QMAX( 0, (firstChar - &string->at(0)) - wordStart );
00386
00387
00388 for ( int hypos = maxlen-1 ; hypos >= minPos ; --hypos )
00389 if ( ( hyphens[hypos] % 2 )
00390 && string->at(hypos + wordStart).format()->hyphenation() )
00391 {
00392 lineStart->hyphenated = true;
00393 lastBreak = hypos + wordStart;
00394 hyphenated = true;
00395 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH) || defined(DEBUG_HYPHENATION)
00396 kdDebug(32500) << "Hyphenation: will break at " << lastBreak << " using tempworddata at position " << hypos << "/" << tempWordData.size() << endl;
00397 #endif
00398 if ( hypos < (int)tempWordData.size() )
00399 {
00400 const TemporaryWordData& twd = tempWordData[ hypos ];
00401 lineStart->baseLine = twd.baseLine;
00402 lineStart->h = twd.height;
00403 tmpWused = twd.lineWidth;
00404 }
00405 break;
00406 }
00407 delete[] hyphens;
00408 }
00409 }
00410
00411
00412 if ( lastBreak < 0 ) {
00413 bool formatLine = true;
00414 if ( c->lineStart )
00415 {
00416
00417
00418 if ( availableWidth > maxAvailableWidth.second )
00419 {
00420 maxAvailableWidth.first = y;
00421 maxAvailableWidth.second = availableWidth;
00422 }
00423
00424
00425
00426 if ( maxY > -1 && parag->rect().y() + y >= maxY )
00427 {
00428 kdDebug(32500) << parag->rect().y() + y << " over maxY=" << maxY
00429 << " -> final choice for the line: y=" << maxAvailableWidth.first << endl;
00430 y = maxAvailableWidth.first;
00431 if ( availableWidth )
00432 Q_ASSERT( maxAvailableWidth.second != 0 );
00433 lineStart->y = y;
00434
00435
00436 }
00437 else
00438 {
00439
00440
00441
00442 y += tmph;
00443 kdDebug(32500) << "KoTextFormatter: moving down empty line by h=" << tmph << ": y=" << y << endl;
00444 formatLine = false;
00445 }
00446 }
00447 if ( formatLine && i > 0 )
00448 {
00449
00450 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00451 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00452 lineStart->h = lineStart->baseLine + belowBaseLine;
00453 lineStart->w = dw;
00454
00455 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c-1, align, availableWidth - x );
00456 lineStart->lineSpacing = parag->lineSpacing( (int)parag->lineStartList().count()-1 );
00457 lineStart->h += lineStart->lineSpacing;
00458 y += lineStart->h;
00459 lineStart = lineStart2;
00460 #ifdef DEBUG_FORMATTER
00461 int linenr = parag->lineStartList().count()-1;
00462 kdDebug(32500) << "line " << linenr << " done (breaking at current char). y now " << y << endl;
00463 #endif
00464 tmph = c->height();
00465
00466 initialRMargin = currentRightMargin;
00467 x = left;
00468 if ( doc )
00469 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00470 ww,
00471 x, initialRMargin, dw, parag );
00472
00473 pixelx = zh->layoutUnitToPixelX( x );
00474 initialHeight = tmph;
00475 initialLMargin = x;
00476 availableWidth = dw - initialRMargin;
00477 if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
00478 int nx = parag->nextTab( i, x );
00479 if ( nx < x )
00480 ww = availableWidth - x;
00481 else
00482 ww = nx - x + 1;
00483 }
00484 if ( x != left || availableWidth != dw )
00485 fullWidth = FALSE;
00486 lineStart->y = y;
00487 parag->insertLineStart( i, lineStart );
00488 tempWordData.clear();
00489 lineStart->baseLine = c->ascent();
00490 lineStart->h = c->height();
00491 c->lineStart = 1;
00492 firstChar = c;
00493 tmpBaseLine = lineStart->baseLine;
00494 lastBreak = -1;
00495 col = 0;
00496
00497 tmpWused = 0;
00498 }
00499
00500
00501
00502 if ( formatLine && maxY > -1 )
00503 {
00504 if ( parag->rect().y() + y < maxY )
00505 {
00506 --i;
00507 continue;
00508 }
00509 else
00510 {
00511 #ifdef DEBUG_FORMATTER
00512 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00513 #endif
00514
00515 abort = true;
00516 }
00517 }
00518
00519
00520 } else {
00521
00522
00523 if ( maxY > -1 && parag->rect().y() + y + lineStart->h >= maxY ) {
00524 #ifdef DEBUG_FORMATTER
00525 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00526 #endif
00527 abort = true;
00528 }
00529 else
00530 {
00531
00532 i = lastBreak;
00533 c = &string->at( i );
00534 int spaceAfterLine = availableWidth - c->x;
00535
00536
00537 spaceAfterLine -= c->width;
00538
00539
00540 if ( c->c.unicode() == 0xad || hyphenated )
00541 {
00542
00543 int width = KoTextZoomHandler::ptToLayoutUnitPt( c->format()->refFontMetrics().width( QChar(0xad) ) );
00544 if ( c->c.unicode() == 0xad )
00545 c->width = width;
00546 spaceAfterLine -= width;
00547 }
00548 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, spaceAfterLine );
00549 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00550 lineStart->w = dw;
00551 lineStart->h += lineStart->lineSpacing;
00552 y += lineStart->h;
00553 lineStart = lineStart2;
00554 #ifdef DEBUG_FORMATTER
00555 kdDebug(32500) << "Breaking at a breakable char (" << i << "). linenr=" << parag->lineStartList().count()-1 << " y=" << y << endl;
00556 #endif
00557
00558 c = &string->at( i + 1 );
00559 #ifdef DEBUG_FORMATTER
00560 kdDebug(32500) << "Next line will start at i+1=" << i+1 << ", char=" << QString(c->c) << endl;
00561 #endif
00562 tmph = c->height();
00563
00564 initialRMargin = currentRightMargin;
00565 x = left;
00566 if ( doc )
00567 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00568 c->width,
00569 x, initialRMargin, dw, parag );
00570
00571 pixelx = zh->layoutUnitToPixelX( x );
00572 initialHeight = tmph;
00573 initialLMargin = x;
00574 availableWidth = dw - initialRMargin;
00575 if ( x != left || availableWidth != dw )
00576 fullWidth = FALSE;
00577 lineStart->y = y;
00578 parag->insertLineStart( i + 1, lineStart );
00579 tempWordData.clear();
00580 lineStart->baseLine = c->ascent();
00581 lineStart->h = c->height();
00582 firstChar = c;
00583 tmpBaseLine = lineStart->baseLine;
00584 lastBreak = -1;
00585 col = 0;
00586
00587 tmpWused = 0;
00588 c->lineStart = 1;
00589 continue;
00590 }
00591 }
00592 } else if ( lineStart && ( settings->isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) {
00593
00594 if ( len <= 2 || i < len - 1 ) {
00595 #ifdef DEBUG_FORMATTER_VERT
00596 kdDebug(32500) << " Breakable character (i=" << i << " len=" << len << "):"
00597 << " combining " << tmpBaseLine << "/" << tmph
00598 << " with " << c->ascent() << "/" << c->height() << endl;
00599 #endif
00600
00601 int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() - c->ascent() );
00602 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
00603 tmph = tmpBaseLine + belowBaseLine;
00604 #ifdef DEBUG_FORMATTER_VERT
00605 kdDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00606 #endif
00607 }
00608 tempWordData.clear();
00609
00610
00611 wused = QMAX( wused, tmpWused );
00612 #ifdef DEBUG_FORMATTER_WIDTH
00613 kdDebug(32500) << " Breakable character (i=" << i << " len=" << len << "): wused=" << wused << endl;
00614 #endif
00615 tmpWused = 0;
00616
00617 #ifdef DEBUG_FORMATTER_VERT
00618 kdDebug(32500) << "Breakable character: combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00619 #endif
00620 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00621 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00622 lineStart->h = lineStart->baseLine + belowBaseLine;
00623 lineStart->w = dw;
00624 #ifdef DEBUG_FORMATTER_VERT
00625 kdDebug(32500) << " -> line baseLine/height : " << lineStart->baseLine << "/" << lineStart->h << endl;
00626 #endif
00627
00628
00629 if ( doc && lineStart->h > initialHeight )
00630 {
00631 bool firstLine = ( firstChar == &string->at( 0 ) );
00632 int newLMargin = leftMargin( firstLine );
00633 int newRMargin = rightMargin( firstLine );
00634 int newPageWidth = dw;
00635 initialHeight = lineStart->h;
00636 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00637 firstChar->width,
00638 newLMargin, newRMargin, newPageWidth, parag );
00639
00640 #ifdef DEBUG_FORMATTER
00641 kdDebug(32500) << "new height: " << initialHeight << " => left=" << left << " first-char=" << (firstChar==&string->at(0)) << " newLMargin=" << newLMargin << " newRMargin=" << newRMargin << endl;
00642 #endif
00643 if ( newLMargin != initialLMargin || newRMargin != initialRMargin || newPageWidth != dw )
00644 {
00645 #ifdef DEBUG_FORMATTER
00646 kdDebug(32500) << "formatting again" << endl;
00647 #endif
00648 i = (firstChar - &string->at(0));
00649 x = newLMargin;
00650 pixelx = zh->layoutUnitToPixelX( x );
00651 availableWidth = dw - newRMargin;
00652 initialLMargin = newLMargin;
00653 initialRMargin = newRMargin;
00654 dw = newPageWidth;
00655 c = &string->at( i );
00656 tmph = c->height();
00657 tmpBaseLine = c->ascent();
00658 lineStart->h = tmph;
00659 lineStart->baseLine = tmpBaseLine;
00660 lastBreak = -1;
00661 col = 0;
00662
00663 #ifdef DEBUG_FORMATTER
00664 kdDebug(32500) << "Restarting with i=" << i << " x=" << x << " y=" << y << " tmph=" << tmph << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " y=" << y << endl;
00665 #endif
00666
00667
00668 ww = c->width;
00669 #ifndef REF_IS_LU
00670 pixelww = c->pixelwidth;
00671 #endif
00672
00673 tmpWused = 0;
00674 }
00675 }
00676
00677
00678 if ( i < len - 2 || c->c != ' ' )
00679 lastBreak = i;
00680
00681 } else {
00682
00683
00684 #ifdef DEBUG_FORMATTER_VERT
00685 kdDebug(32500) << " Non-breakable character: combining " << tmpBaseLine << "/" << tmph << " with " << c->ascent() << "/" << c->height() << endl;
00686 #endif
00687
00688 int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() - c->ascent() );
00689 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
00690 tmph = tmpBaseLine + belowBaseLine;
00691 #ifdef DEBUG_FORMATTER_VERT
00692 kdDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00693 #endif
00694
00695 TemporaryWordData twd;
00696 twd.baseLine = tmpBaseLine;
00697 twd.height = tmph;
00698 twd.lineWidth = tmpWused;
00699 tempWordData.append( twd );
00700 }
00701
00702 c->x = x;
00703
00704
00705 c->pixelxadj = pixelx - zh->layoutUnitToPixelX( x );
00706
00707 #ifdef DEBUG_FORMATTER
00708 kdDebug(32500) << "LU: x=" << x << " [equiv. to pix=" << zh->layoutUnitToPixelX( x ) << "] ; PIX: x=" << pixelx << " --> adj=" << c->pixelxadj << endl;
00709 #endif
00710
00711 x += ww;
00712
00713 if ( i > 0 )
00714 lastChr->pixelwidth = pixelx - lastPixelx;
00715 if ( i < len - 1 )
00716 tmpWused = QMAX( tmpWused, x );
00717 else
00718 c->pixelwidth = zh->layoutUnitToPixelX( ww );
00719
00720 lastPixelx = pixelx;
00721 #ifdef REF_IS_LU
00722 pixelx = zh->layoutUnitToPixelX( x );
00723 #else
00724 pixelx += pixelww;
00725 #endif
00726 #ifdef DEBUG_FORMATTER
00727 kdDebug(32500) << "LU: added " << ww << " -> now x=" << x << " ; PIX: added " << pixelww << " -> now pixelx=" << pixelx << endl;
00728 #endif
00729 }
00730
00731
00732
00733 if ( len > 1 ) {
00734 c->format()->removeRef();
00735 c->setFormat( string->at( len - 2 ).format() );
00736 c->format()->addRef();
00737 }
00738
00739
00740 if ( lineStart ) {
00741 #ifdef DEBUG_FORMATTER
00742 kdDebug(32500) << "Last Line.... linenr=" << (int)parag->lineStartList().count()-1 << endl;
00743 #endif
00744 #ifdef DEBUG_FORMATTER_VERT
00745 kdDebug(32500) << "Last Line... Combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00746 #endif
00747
00748 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00749 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00750 lineStart->h = lineStart->baseLine + belowBaseLine;
00751 lineStart->w = dw;
00752 #ifdef DEBUG_FORMATTER_WIDTH
00753 kdDebug(32500) << "Last line: w = dw = " << dw << endl;
00754 #endif
00755 #ifdef DEBUG_FORMATTER_VERT
00756 kdDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl;
00757 #endif
00758
00759 if ( align == Qt::AlignJustify )
00760 align = Qt::AlignAuto;
00761 int space = availableWidth - x + c->width;
00762 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, space );
00763 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00764 lineStart->h += lineStart->lineSpacing;
00765 delete lineStart2;
00766 }
00767
00768
00769 wused = QMAX( wused, tmpWused );
00770 #ifdef DEBUG_FORMATTER_WIDTH
00771 kdDebug(32500) << "Done, wused=" << wused << endl;
00772 #endif
00773
00774 int m = parag->bottomMargin();
00775
00776
00777
00778 parag->setFullWidth( fullWidth );
00779
00780
00781 #ifdef DEBUG_FORMATTER_VERT
00782 kdDebug(32500) << "Adding height of last line(" << lineStart->h << ") and bottomMargin(" << m << ") to y(" << y << ") => " << y+lineStart->h+m << endl;
00783 #endif
00784 y += lineStart->h + m;
00785
00786 tmpWused += currentRightMargin;
00787
00788
00789
00790
00791 #ifdef DEBUG_FORMATTER
00792
00793 int numberOfLines = 0;
00794 QString charPosList;
00795 for ( int i = 0 ; i < len; ++i ) {
00796 KoTextStringChar *chr = &string->at( i );
00797 if ( i == 0 )
00798 assert( chr->lineStart );
00799 if ( chr->lineStart ) {
00800 ++numberOfLines;
00801 charPosList += QString::number(i) + " ";
00802 }
00803 }
00804 kdDebug(32500) << parag->lineStartList().count() << " lines. " << numberOfLines << " chars with lineStart set: " << charPosList << endl;
00805 assert( numberOfLines == (int)parag->lineStartList().count() );
00806 #endif
00807 return !abort;
00808 }
00809
00810
00811 void KoTextFormatterCore::moveChar( KoTextStringChar& chr, KoZoomHandler *zh,
00812 int deltaX, int deltaPixelX )
00813 {
00814 #ifndef REF_IS_LU
00815 int pixelx = chr.pixelxadj + zh->layoutUnitToPixelX( chr.x );
00816 #endif
00817 chr.x += deltaX;
00818 #ifndef REF_IS_LU
00819 chr.pixelxadj = pixelx + deltaPixelX - zh->layoutUnitToPixelX( chr.x );
00820 #endif
00821 }
00822
00823 KoTextParagLineStart *KoTextFormatterCore::koFormatLine(
00824 KoZoomHandler *zh,
00825 KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
00826 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00827 {
00828 if( string->isBidi() )
00829 return koBidiReorderLine( zh, parag, string, line, startChar, lastChar, align, space );
00830 space = QMAX( space, 0 );
00831 int start = (startChar - &string->at(0));
00832 int last = (lastChar - &string->at(0) );
00833 #ifdef INDIC
00834
00835 KoTextStringChar *ch = lastChar;
00836 while ( ch > startChar && ch->whiteSpace ) {
00837 space += ch->format()->width( ' ' );
00838 --ch;
00839 }
00840
00841 if (space < 0)
00842 space = 0;
00843
00844 #endif
00845
00846 if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
00847 if ( align & Qt::AlignHCenter )
00848 space /= 2;
00849 int toAddPix = zh->layoutUnitToPixelX( space );
00850 for ( int j = last; j >= start; --j ) {
00851 KoTextStringChar &chr = string->at( j );
00853 if ( chr.c == '\t' ) {
00854 break;
00855 }
00856 moveChar( chr, zh, space, toAddPix );
00857 }
00858 } else if ( align & Qt::AlignJustify ) {
00859 int numSpaces = 0;
00860
00861 for ( int j = last-1; j >= start; --j ) {
00863 if ( string->at( j ).c == '\t' ) {
00864 start = j+1;
00865 break;
00866 }
00867 if( settings->isStretchable( string, j ) ) {
00868 numSpaces++;
00869 }
00870 }
00871 int toAdd = 0;
00872 int toAddPix = 0;
00873 for ( int k = start + 1; k <= last; ++k ) {
00874 KoTextStringChar &chr = string->at( k );
00875 if ( toAdd != 0 )
00876 moveChar( chr, zh, toAdd, toAddPix );
00877 if( settings->isStretchable( string, k ) && numSpaces ) {
00878 int s = space / numSpaces;
00879 toAdd += s;
00880 toAddPix = zh->layoutUnitToPixelX( toAdd );
00881 space -= s;
00882 numSpaces--;
00883 chr.width += s;
00884 #ifndef REF_IS_LU
00885 chr.pixelwidth += zh->layoutUnitToPixelX( s );
00886 #endif
00887 }
00888 }
00889 }
00890 int current=0;
00891 int nc=0;
00892 KoTextFormat refFormat( *string->at(0).format() );
00893 for(int i=start;i<=last;++i)
00894 {
00895 KoTextFormat* format=string->at(i).format();
00896
00897 if ( (((!format->underline())&&
00898 (!format->doubleUnderline())&&
00899 (!format->waveUnderline())&&
00900 (format->underlineType()!=KoTextFormat::U_SIMPLE_BOLD))
00901 || i == last)
00902 && nc )
00903 {
00904 double avg=static_cast<double>(current)/nc;
00905 avg/=18.0;
00906
00907 refFormat.setUnderLineWidth( avg );
00908 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth );
00909 nc=0;
00910 current=0;
00911 }
00912
00913 else if(format->underline()||
00914 format->waveUnderline()||
00915 format->doubleUnderline()||
00916 (format->underlineType() == KoTextFormat::U_SIMPLE_BOLD))
00917 {
00918 ++nc;
00919 current += format->pointSize();
00920 }
00921 }
00922 #if 0
00923 if ( last >= 0 && last < string->length() ) {
00924 KoTextStringChar &chr = string->at( last );
00925 line->w = chr.x + chr.width;
00926
00927 if ( line->hyphenated )
00928 line->w += KoTextZoomHandler::ptToLayoutUnitPt( chr.format()->refFontMetrics().width( QChar(0xad) ) );
00929 } else
00930 line->w = 0;
00931 #endif
00932
00933 return new KoTextParagLineStart();
00934 }
00935
00936
00937 KoTextParagLineStart *KoTextFormatterCore::koBidiReorderLine(
00938 KoZoomHandler *zh,
00939 KoTextParag * , KoTextString *text, KoTextParagLineStart *line,
00940 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00941 {
00942
00943
00944 #if 0
00945
00946 int endSpaces = 0;
00947 while ( lastChar > startChar && lastChar->whiteSpace ) {
00948 space += lastChar->format()->width( ' ' );
00949 --lastChar;
00950 ++endSpaces;
00951 }
00952 #endif
00953
00954 int start = (startChar - &text->at(0));
00955 int last = (lastChar - &text->at(0) );
00956 #ifdef DEBUG_FORMATTER
00957 kdDebug(32500) << "*KoTextFormatter::koBidiReorderLine from " << start << " to " << last << " space=" << space << " startChar->x=" << startChar->x << endl;
00958 #endif
00959 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
00960 QString str;
00961 str.setUnicode( 0, last - start + 1 );
00962
00963 KoTextStringChar *ch = startChar;
00964 QChar *qch = (QChar *)str.unicode();
00965 while ( ch <= lastChar ) {
00966 *qch = ch->c;
00967 qch++;
00968 ch++;
00969 }
00970 int x = startChar->x;
00971
00972 QPtrList<KoTextRun> *runs;
00973 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
00974 (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
00975
00976
00977
00978 int numSpaces = 0;
00979
00980 if( align == Qt::AlignAuto ) {
00981
00982 if ( text->isRightToLeft() )
00983 align = Qt::AlignRight;
00984 }
00985
00986 if ( align & Qt::AlignHCenter ) {
00987 x += space/2;
00988 } else if ( align & Qt::AlignRight ) {
00989 x += space;
00990 } else if ( align & Qt::AlignJustify ) {
00991 for ( int j = last - 1; j >= start; --j ) {
00993 if ( text->at( j ).c == '\t' ) {
00994 start = j+1;
00995 break;
00996 }
00997 if( settings->isStretchable( text, j ) ) {
00998 numSpaces++;
00999 }
01000 }
01001 }
01002
01003 int pixelx = zh->layoutUnitToPixelX( x );
01004 int toAdd = 0;
01005 int toAddPix = 0;
01006 bool first = TRUE;
01007 KoTextRun *r = runs->first();
01008 int xmax = -0xffffff;
01009 while ( r ) {
01010 #ifdef DEBUG_FORMATTER
01011 kdDebug(32500) << "koBidiReorderLine level: " << r->level << endl;
01012 #endif
01013 if(r->level %2) {
01014
01015 int pos = r->stop + start;
01016 while(pos >= r->start + start) {
01017 KoTextStringChar &chr = text->at(pos);
01018 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01019 int s = space / numSpaces;
01020 toAdd += s;
01021 toAddPix = zh->layoutUnitToPixelX( toAdd );
01022 space -= s;
01023 numSpaces--;
01024 chr.width += s;
01025 chr.pixelwidth += zh->layoutUnitToPixelX( s );
01026 } else if ( first ) {
01027 first = FALSE;
01028 if ( chr.c == ' ' )
01029 {
01030
01031 x -= chr.width;
01032 pixelx -= chr.pixelwidth;
01033 }
01034 }
01035 chr.x = x + toAdd;
01036 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01037 #ifdef DEBUG_FORMATTER
01038 kdDebug(32500) << "koBidiReorderLine: pos=" << pos << " x(LU)=" << x << " toAdd(LU)=" << toAdd << " -> chr.x=" << chr.x << " pixelx=" << pixelx << "+" << zh->layoutUnitToPixelX( toAdd ) << ", pixelxadj=" << pixelx+zh->layoutUnitToPixelX( toAdd )-zh->layoutUnitToPixelX( chr.x ) << endl;
01039 #endif
01040 chr.rightToLeft = TRUE;
01041 chr.startOfRun = FALSE;
01042 int ww = chr.width;
01043 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01044 x += ww;
01045 pixelx += chr.pixelwidth;
01046 #ifdef DEBUG_FORMATTER
01047 kdDebug(32500) << " ww=" << ww << " adding to x, now " << x << ". pixelwidth=" << chr.pixelwidth << " adding to pixelx, now " << pixelx << " xmax=" << xmax << endl;
01048 #endif
01049 pos--;
01050 }
01051 } else {
01052 int pos = r->start + start;
01053 while(pos <= r->stop + start) {
01054 KoTextStringChar& chr = text->at(pos);
01055 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01056 int s = space / numSpaces;
01057 toAdd += s;
01058 toAddPix = zh->layoutUnitToPixelX( toAdd );
01059 space -= s;
01060 numSpaces--;
01061 } else if ( first ) {
01062 first = FALSE;
01063 if ( chr.c == ' ' )
01064 {
01065
01066 x -= chr.width;
01067 pixelx -= chr.pixelwidth;
01068 }
01069 }
01070 chr.x = x + toAdd;
01071 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01072 chr.rightToLeft = FALSE;
01073 chr.startOfRun = FALSE;
01074 int ww = chr.width;
01075
01076 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01077 x += ww;
01078 pixelx += chr.pixelwidth;
01079 pos++;
01080 }
01081 }
01082 text->at( r->start + start ).startOfRun = TRUE;
01083 r = runs->next();
01084 }
01085
01086
01087 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01088 delete control;
01089 delete runs;
01090 return ls;
01091 }
01092
01093 void KoTextFormatter::postFormat( KoTextParag* parag )
01094 {
01095 parag->fixParagWidth( viewFormattingChars() );
01096 }