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