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