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 const bool emptyLine = c->lineStart;
00419 if ( emptyLine )
00420 {
00421
00422
00423
00424
00425
00426
00427
00428 if ( availableWidth > maxAvailableWidth.second )
00429 {
00430 maxAvailableWidth.first = y;
00431 maxAvailableWidth.second = availableWidth;
00432 }
00433
00434
00435 if ( ( maxY > -1 && parag->rect().y() + y >= maxY ) || tmph <= 0 )
00436 {
00437
00438
00439
00440
00441
00442 if ( c->width >= doc->flow()->width() )
00443 {
00444
00445 kdDebug(32500) << parag->rect().y() + y << " over maxY=" << maxY
00446 << " -> final choice for the line: y=" << maxAvailableWidth.first << endl;
00447 y = maxAvailableWidth.first;
00448 if ( availableWidth )
00449 Q_ASSERT( maxAvailableWidth.second != 0 );
00450 lineStart->y = y;
00451 maxAvailableWidth = qMakePair( 0, 0 );
00452 }
00453 else
00454 {
00455
00456 #ifdef DEBUG_FORMATTER
00457 if ( tmph <= 0 )
00458 kdDebug(32500) << "Line has a height of " << tmph << ", let's stop." << endl;
00459 else
00460 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00461 #endif
00462
00463 abort = true;
00464 }
00465 }
00466 else
00467 {
00468
00469
00470
00471 y += tmph;
00472 kdDebug(32500) << "KoTextFormatter: moving down empty line by h=" << tmph << ": y=" << y << endl;
00473
00474 --i;
00475 continue;
00476 }
00477 }
00478 if ( !emptyLine && i > 0 )
00479 {
00480
00481 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00482 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00483 lineStart->h = lineStart->baseLine + belowBaseLine;
00484 lineStart->w = dw;
00485
00486 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c-1, align, availableWidth - x );
00487 y += lineStart->h;
00488 lineStart = lineStart2;
00489 #ifdef DEBUG_FORMATTER
00490 int linenr = parag->lineStartList().count()-1;
00491 kdDebug(32500) << "line " << linenr << " done (breaking at current char). y now " << y << endl;
00492 #endif
00493 tmph = c->height();
00494
00495 initialRMargin = currentRightMargin;
00496 x = left;
00497 if ( doc )
00498 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00499 ww,
00500 x, initialRMargin, dw, parag );
00501
00502 pixelx = zh->layoutUnitToPixelX( x );
00503 initialHeight = tmph;
00504 initialLMargin = x;
00505 availableWidth = dw - initialRMargin;
00506 if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
00507 int nx = parag->nextTab( i, x, availableWidth );
00508 if ( nx < x )
00509 ww = availableWidth - x;
00510 else
00511 ww = nx - x;
00512 }
00513 if ( x != left || availableWidth != dw )
00514 fullWidth = FALSE;
00515 lineStart->y = y;
00516 parag->insertLineStart( i, lineStart );
00517 tempWordData.clear();
00518 lineStart->baseLine = c->ascent();
00519 lineStart->h = c->height();
00520 c->lineStart = 1;
00521 firstChar = c;
00522 tmpBaseLine = lineStart->baseLine;
00523 lastBreak = -1;
00524 col = 0;
00525
00526 tmpWused = 0;
00527 }
00528
00529
00530
00531
00532 if ( !emptyLine && maxY > -1 )
00533 {
00534 if ( parag->rect().y() + y < maxY )
00535 {
00536 #ifdef DEBUG_FORMATTER
00537 kdDebug(32500) << "Re-checking formatting for character " << i << endl;
00538 #endif
00539 --i;
00540 continue;
00541 }
00542 else
00543 {
00544 #ifdef DEBUG_FORMATTER
00545 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00546 #endif
00547
00548 abort = true;
00549 }
00550 }
00551
00552
00553 } else {
00554
00555
00556 if ( maxY > -1 && parag->rect().y() + y + lineStart->h >= maxY ) {
00557 #ifdef DEBUG_FORMATTER
00558 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00559 #endif
00560 abort = true;
00561 }
00562 else
00563 {
00564
00565 i = lastBreak;
00566 c = &string->at( i );
00567 int spaceAfterLine = availableWidth - c->x;
00568
00569
00570 spaceAfterLine -= c->width;
00571
00572
00573 if ( c->c.unicode() == 0xad || hyphenated )
00574 {
00575
00576 int width = KoTextZoomHandler::ptToLayoutUnitPt( c->format()->refFontMetrics().width( QChar(0xad) ) );
00577 if ( c->c.unicode() == 0xad )
00578 c->width = width;
00579 spaceAfterLine -= width;
00580 }
00581 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, spaceAfterLine );
00582 lineStart->w = dw;
00583 y += lineStart->h;
00584 lineStart = lineStart2;
00585 #ifdef DEBUG_FORMATTER
00586 kdDebug(32500) << "Breaking at a breakable char (" << i << "). linenr=" << parag->lineStartList().count()-1 << " y=" << y << endl;
00587 #endif
00588
00589 c = &string->at( i + 1 );
00590 #ifdef DEBUG_FORMATTER
00591 kdDebug(32500) << "Next line will start at i+1=" << i+1 << ", char=" << QString(c->c) << endl;
00592 #endif
00593 tmph = c->height();
00594
00595 initialRMargin = currentRightMargin;
00596 x = left;
00597 if ( doc )
00598 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00599 c->width,
00600 x, initialRMargin, dw, parag );
00601
00602 pixelx = zh->layoutUnitToPixelX( x );
00603 initialHeight = tmph;
00604 initialLMargin = x;
00605 availableWidth = dw - initialRMargin;
00606 if ( x != left || availableWidth != dw )
00607 fullWidth = FALSE;
00608 lineStart->y = y;
00609 parag->insertLineStart( i + 1, lineStart );
00610 tempWordData.clear();
00611 lineStart->baseLine = c->ascent();
00612 lineStart->h = c->height();
00613 firstChar = c;
00614 tmpBaseLine = lineStart->baseLine;
00615 lastBreak = -1;
00616 col = 0;
00617
00618 tmpWused = 0;
00619 c->lineStart = 1;
00620 continue;
00621 }
00622 }
00623 } else if ( lineStart && ( settings->isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) {
00624
00625 if ( len <= 2 || i < len - 1 ) {
00626 #ifdef DEBUG_FORMATTER_VERT
00627 kdDebug(32500) << " Breakable character (i=" << i << " len=" << len << "):"
00628 << " combining " << tmpBaseLine << "/" << tmph
00629 << " with " << c->ascent() << "/" << c->height() << endl;
00630 #endif
00631
00632 int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() - c->ascent() );
00633 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
00634 tmph = tmpBaseLine + belowBaseLine;
00635 #ifdef DEBUG_FORMATTER_VERT
00636 kdDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00637 #endif
00638 }
00639 tempWordData.clear();
00640
00641
00642 wused = QMAX( wused, tmpWused );
00643 #ifdef DEBUG_FORMATTER_WIDTH
00644 kdDebug(32500) << " Breakable character (i=" << i << " len=" << len << "): wused=" << wused << endl;
00645 #endif
00646 tmpWused = 0;
00647
00648 #ifdef DEBUG_FORMATTER_VERT
00649 kdDebug(32500) << "Breakable character: combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00650 #endif
00651 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00652 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00653 lineStart->h = lineStart->baseLine + belowBaseLine;
00654 lineStart->w = dw;
00655 #ifdef DEBUG_FORMATTER_VERT
00656 kdDebug(32500) << " -> line baseLine/height : " << lineStart->baseLine << "/" << lineStart->h << endl;
00657 #endif
00658
00659
00660 if ( doc && lineStart->h > initialHeight )
00661 {
00662 bool firstLine = ( firstChar == &string->at( 0 ) );
00663 int newLMargin = leftMargin( firstLine );
00664 int newRMargin = rightMargin( firstLine );
00665 int newPageWidth = dw;
00666 initialHeight = lineStart->h;
00667 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00668 firstChar->width,
00669 newLMargin, newRMargin, newPageWidth, parag );
00670
00671 #ifdef DEBUG_FORMATTER
00672 kdDebug(32500) << "new height: " << initialHeight << " => left=" << left << " first-char=" << (firstChar==&string->at(0)) << " newLMargin=" << newLMargin << " newRMargin=" << newRMargin << endl;
00673 #endif
00674 if ( newLMargin != initialLMargin || newRMargin != initialRMargin || newPageWidth != dw )
00675 {
00676 #ifdef DEBUG_FORMATTER
00677 kdDebug(32500) << "formatting again" << endl;
00678 #endif
00679 i = (firstChar - &string->at(0));
00680 x = newLMargin;
00681 pixelx = zh->layoutUnitToPixelX( x );
00682 availableWidth = dw - newRMargin;
00683 initialLMargin = newLMargin;
00684 initialRMargin = newRMargin;
00685 dw = newPageWidth;
00686 c = &string->at( i );
00687 tmph = c->height();
00688 tmpBaseLine = c->ascent();
00689 lineStart->h = tmph;
00690 lineStart->baseLine = tmpBaseLine;
00691 lastBreak = -1;
00692 col = 0;
00693
00694 #ifdef DEBUG_FORMATTER
00695 kdDebug(32500) << "Restarting with i=" << i << " x=" << x << " y=" << y << " tmph=" << tmph << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " y=" << y << endl;
00696 #endif
00697
00698
00699 ww = c->width;
00700 #ifndef REF_IS_LU
00701 pixelww = c->pixelwidth;
00702 #endif
00703
00704 tmpWused = 0;
00705 }
00706 }
00707
00708
00709 if ( i < len - 2 || c->c != ' ' )
00710 lastBreak = i;
00711
00712 } else if ( i < len - 1 ) {
00713
00714
00715 #ifdef DEBUG_FORMATTER_VERT
00716 kdDebug(32500) << " Non-breakable character: combining " << tmpBaseLine << "/" << tmph << " with " << c->ascent() << "/" << c->height() << endl;
00717 #endif
00718
00719 int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() - c->ascent() );
00720 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
00721 tmph = tmpBaseLine + belowBaseLine;
00722 #ifdef DEBUG_FORMATTER_VERT
00723 kdDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00724 #endif
00725
00726 TemporaryWordData twd;
00727 twd.baseLine = tmpBaseLine;
00728 twd.height = tmph;
00729 twd.lineWidth = tmpWused;
00730 tempWordData.append( twd );
00731 }
00732
00733 c->x = x;
00734
00735
00736 c->pixelxadj = pixelx - zh->layoutUnitToPixelX( x );
00737
00738 #ifdef DEBUG_FORMATTER
00739 kdDebug(32500) << "LU: x=" << x << " [equiv. to pix=" << zh->layoutUnitToPixelX( x ) << "] ; PIX: x=" << pixelx << " --> adj=" << c->pixelxadj << endl;
00740 #endif
00741
00742 x += ww;
00743
00744 if ( i > 0 )
00745 lastChr->pixelwidth = pixelx - lastPixelx;
00746 if ( i < len - 1 )
00747 tmpWused = QMAX( tmpWused, x );
00748 else
00749 c->pixelwidth = zh->layoutUnitToPixelX( ww );
00750
00751 lastPixelx = pixelx;
00752 #ifdef REF_IS_LU
00753 pixelx = zh->layoutUnitToPixelX( x );
00754 #else
00755 pixelx += pixelww;
00756 #endif
00757 #ifdef DEBUG_FORMATTER
00758 kdDebug(32500) << "LU: added " << ww << " -> now x=" << x << " ; PIX: added " << pixelww << " -> now pixelx=" << pixelx << endl;
00759 #endif
00760 }
00761
00762
00763
00764 if ( len > 1 ) {
00765 c->format()->removeRef();
00766 c->setFormat( string->at( len - 2 ).format() );
00767 c->format()->addRef();
00768 }
00769
00770
00771 if ( lineStart ) {
00772 #ifdef DEBUG_FORMATTER
00773 kdDebug(32500) << "Last Line.... linenr=" << (int)parag->lineStartList().count()-1 << endl;
00774 #endif
00775 #ifdef DEBUG_FORMATTER_VERT
00776 kdDebug(32500) << "Last Line... Combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00777 #endif
00778
00779 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00780 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00781 lineStart->h = lineStart->baseLine + belowBaseLine;
00782 lineStart->w = dw;
00783 #ifdef DEBUG_FORMATTER_WIDTH
00784 kdDebug(32500) << "Last line: w = dw = " << dw << endl;
00785 #endif
00786 #ifdef DEBUG_FORMATTER_VERT
00787 kdDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl;
00788 #endif
00789
00790 if ( align == Qt::AlignJustify )
00791 align = Qt::AlignAuto;
00792 int space = availableWidth - x + c->width;
00793 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, space );
00794 delete lineStart2;
00795 }
00796
00797
00798 wused = QMAX( wused, tmpWused );
00799 #ifdef DEBUG_FORMATTER_WIDTH
00800 kdDebug(32500) << "Done, wused=" << wused << endl;
00801 #endif
00802
00803 int m = parag->bottomMargin();
00804
00805
00806
00807 parag->setFullWidth( fullWidth );
00808
00809
00810 #ifdef DEBUG_FORMATTER_VERT
00811 kdDebug(32500) << "Adding height of last line(" << lineStart->h << ") and bottomMargin(" << m << ") to y(" << y << ") => " << y+lineStart->h+m << endl;
00812 #endif
00813 y += lineStart->h + m;
00814
00815 tmpWused += currentRightMargin;
00816
00817
00818
00819
00820 #ifdef DEBUG_FORMATTER
00821
00822 int numberOfLines = 0;
00823 QString charPosList;
00824 for ( int i = 0 ; i < len; ++i ) {
00825 KoTextStringChar *chr = &string->at( i );
00826 if ( i == 0 )
00827 assert( chr->lineStart );
00828 if ( chr->lineStart ) {
00829 ++numberOfLines;
00830 charPosList += QString::number(i) + " ";
00831 }
00832 }
00833 kdDebug(32500) << parag->lineStartList().count() << " lines. " << numberOfLines << " chars with lineStart set: " << charPosList << endl;
00834 assert( numberOfLines == (int)parag->lineStartList().count() );
00835 #endif
00836 return !abort;
00837 }
00838
00839
00840 void KoTextFormatterCore::moveChar( KoTextStringChar& chr, KoTextZoomHandler *zh,
00841 int deltaX, int deltaPixelX )
00842 {
00843 #ifndef REF_IS_LU
00844 int pixelx = chr.pixelxadj + zh->layoutUnitToPixelX( chr.x );
00845 #endif
00846 chr.x += deltaX;
00847 #ifndef REF_IS_LU
00848 chr.pixelxadj = pixelx + deltaPixelX - zh->layoutUnitToPixelX( chr.x );
00849 #endif
00850 }
00851
00852 KoTextParagLineStart *KoTextFormatterCore::koFormatLine(
00853 KoTextZoomHandler *zh,
00854 KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
00855 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00856 {
00857 KoTextParagLineStart* ret = 0;
00858 if( string->isBidi() ) {
00859 ret = koBidiReorderLine( zh, parag, string, line, startChar, lastChar, align, space );
00860 } else {
00861 int start = (startChar - &string->at(0));
00862 int last = (lastChar - &string->at(0) );
00863
00864 if (space < 0)
00865 space = 0;
00866
00867
00868 if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
00869 if ( align & Qt::AlignHCenter )
00870 space /= 2;
00871 int toAddPix = zh->layoutUnitToPixelX( space );
00872 for ( int j = last; j >= start; --j ) {
00873 KoTextStringChar &chr = string->at( j );
00874 moveChar( chr, zh, space, toAddPix );
00875 }
00876 } else if ( align & Qt::AlignJustify ) {
00877 int numSpaces = 0;
00878
00879 for ( int j = last-1; j >= start; --j ) {
00881 if ( string->at( j ).c == '\t' ) {
00882 start = j+1;
00883 break;
00884 }
00885 if( settings->isStretchable( string, j ) ) {
00886 numSpaces++;
00887 }
00888 }
00889 int toAdd = 0;
00890 int toAddPix = 0;
00891 for ( int k = start + 1; k <= last; ++k ) {
00892 KoTextStringChar &chr = string->at( k );
00893 if ( toAdd != 0 )
00894 moveChar( chr, zh, toAdd, toAddPix );
00895 if( settings->isStretchable( string, k ) && numSpaces ) {
00896 int s = space / numSpaces;
00897 toAdd += s;
00898 toAddPix = zh->layoutUnitToPixelX( toAdd );
00899 space -= s;
00900 numSpaces--;
00901 chr.width += s;
00902 #ifndef REF_IS_LU
00903 chr.pixelwidth += zh->layoutUnitToPixelX( s );
00904 #endif
00905 }
00906 }
00907 }
00908 int current=0;
00909 int nc=0;
00910 KoTextFormat refFormat( *string->at(0).format() );
00911 for(int i=start;i<=last;++i)
00912 {
00913 KoTextFormat* format=string->at(i).format();
00914
00915 if ( (((!format->underline())&&
00916 (!format->doubleUnderline())&&
00917 (!format->waveUnderline())&&
00918 (format->underlineType()!=KoTextFormat::U_SIMPLE_BOLD))
00919 || i == last)
00920 && nc )
00921 {
00922 double avg=static_cast<double>(current)/nc;
00923 avg/=18.0;
00924
00925 refFormat.setUnderLineWidth( avg );
00926 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth );
00927 nc=0;
00928 current=0;
00929 }
00930
00931 else if(format->underline()||
00932 format->waveUnderline()||
00933 format->doubleUnderline()||
00934 (format->underlineType() == KoTextFormat::U_SIMPLE_BOLD))
00935 {
00936 ++nc;
00937 current += format->pointSize();
00938 }
00939 }
00940 #if 0
00941 if ( last >= 0 && last < string->length() ) {
00942 KoTextStringChar &chr = string->at( last );
00943 line->w = chr.x + chr.width;
00944
00945 if ( line->hyphenated )
00946 line->w += KoTextZoomHandler::ptToLayoutUnitPt( chr.format()->refFontMetrics().width( QChar(0xad) ) );
00947 } else
00948 line->w = 0;
00949 #endif
00950
00951 ret = new KoTextParagLineStart();
00952 }
00953
00954
00955 const int start = (startChar - &string->at(0));
00956 const int last = (lastChar - &string->at(0) );
00957 line->lineSpacing = parag->calculateLineSpacing( (int)parag->lineStartList().count()-1, start, last );
00958 line->h += line->lineSpacing;
00959
00960 return ret;
00961 }
00962
00963
00964 KoTextParagLineStart *KoTextFormatterCore::koBidiReorderLine(
00965 KoTextZoomHandler *zh,
00966 KoTextParag * , KoTextString *text, KoTextParagLineStart *line,
00967 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00968 {
00969
00970
00971 #if 0
00972
00973 int endSpaces = 0;
00974 while ( lastChar > startChar && lastChar->whiteSpace ) {
00975 space += lastChar->format()->width( ' ' );
00976 --lastChar;
00977 ++endSpaces;
00978 }
00979 #endif
00980
00981 int start = (startChar - &text->at(0));
00982 int last = (lastChar - &text->at(0) );
00983 #ifdef DEBUG_FORMATTER
00984 kdDebug(32500) << "*KoTextFormatter::koBidiReorderLine from " << start << " to " << last << " space=" << space << " startChar->x=" << startChar->x << endl;
00985 #endif
00986 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
00987 QString str;
00988 str.setUnicode( 0, last - start + 1 );
00989
00990 KoTextStringChar *ch = startChar;
00991 QChar *qch = (QChar *)str.unicode();
00992 while ( ch <= lastChar ) {
00993 *qch = ch->c;
00994 qch++;
00995 ch++;
00996 }
00997 int x = startChar->x;
00998
00999 QPtrList<KoTextRun> *runs;
01000 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01001 (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01002
01003
01004
01005 int numSpaces = 0;
01006
01007 if( align == Qt::AlignAuto ) {
01008
01009 if ( text->isRightToLeft() )
01010 align = Qt::AlignRight;
01011 }
01012
01013 if ( align & Qt::AlignHCenter ) {
01014 x += space/2;
01015 } else if ( align & Qt::AlignRight ) {
01016 x += space;
01017 } else if ( align & Qt::AlignJustify ) {
01018 for ( int j = last - 1; j >= start; --j ) {
01020 if ( text->at( j ).c == '\t' ) {
01021 start = j+1;
01022 break;
01023 }
01024 if( settings->isStretchable( text, j ) ) {
01025 numSpaces++;
01026 }
01027 }
01028 }
01029
01030 int pixelx = zh->layoutUnitToPixelX( x );
01031 int toAdd = 0;
01032 int toAddPix = 0;
01033 bool first = TRUE;
01034 KoTextRun *r = runs->first();
01035 int xmax = -0xffffff;
01036 while ( r ) {
01037 #ifdef DEBUG_FORMATTER
01038 kdDebug(32500) << "koBidiReorderLine level: " << r->level << endl;
01039 #endif
01040 if(r->level %2) {
01041
01042 int pos = r->stop + start;
01043 while(pos >= r->start + start) {
01044 KoTextStringChar &chr = text->at(pos);
01045 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01046 int s = space / numSpaces;
01047 toAdd += s;
01048 toAddPix = zh->layoutUnitToPixelX( toAdd );
01049 space -= s;
01050 numSpaces--;
01051 chr.width += s;
01052 chr.pixelwidth += zh->layoutUnitToPixelX( s );
01053 } else if ( first ) {
01054 first = FALSE;
01055 if ( chr.c == ' ' )
01056 {
01057
01058 x -= chr.width;
01059 pixelx -= chr.pixelwidth;
01060 }
01061 }
01062 chr.x = x + toAdd;
01063 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01064 #ifdef DEBUG_FORMATTER
01065 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;
01066 #endif
01067 chr.rightToLeft = TRUE;
01068 chr.startOfRun = FALSE;
01069 int ww = chr.width;
01070 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01071 x += ww;
01072 pixelx += chr.pixelwidth;
01073 #ifdef DEBUG_FORMATTER
01074 kdDebug(32500) << " ww=" << ww << " adding to x, now " << x << ". pixelwidth=" << chr.pixelwidth << " adding to pixelx, now " << pixelx << " xmax=" << xmax << endl;
01075 #endif
01076 pos--;
01077 }
01078 } else {
01079 int pos = r->start + start;
01080 while(pos <= r->stop + start) {
01081 KoTextStringChar& chr = text->at(pos);
01082 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01083 int s = space / numSpaces;
01084 toAdd += s;
01085 toAddPix = zh->layoutUnitToPixelX( toAdd );
01086 space -= s;
01087 numSpaces--;
01088 } else if ( first ) {
01089 first = FALSE;
01090 if ( chr.c == ' ' )
01091 {
01092
01093 x -= chr.width;
01094 pixelx -= chr.pixelwidth;
01095 }
01096 }
01097 chr.x = x + toAdd;
01098 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01099 chr.rightToLeft = FALSE;
01100 chr.startOfRun = FALSE;
01101 int ww = chr.width;
01102
01103 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01104 x += ww;
01105 pixelx += chr.pixelwidth;
01106 pos++;
01107 }
01108 }
01109 text->at( r->start + start ).startOfRun = TRUE;
01110 r = runs->next();
01111 }
01112
01113
01114 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01115 delete control;
01116 delete runs;
01117 return ls;
01118 }
01119
01120 void KoTextFormatter::postFormat( KoTextParag* parag )
01121 {
01122 parag->fixParagWidth( viewFormattingChars() );
01123 }