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 "kozoomhandler.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 KoZoomHandler *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 );
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 ) const
00126 {
00127 int left = parag->leftMargin() + doc->leftMargin() ;
00128 if ( firstLine && !parag->string()->isRightToLeft() )
00129 {
00130 left += parag->firstLineMargin();
00131
00132 if( parag->counter() &&
00133 ( parag->counter()->alignment() == Qt::AlignLeft ||
00134 parag->counter()->alignment() == Qt::AlignAuto ) )
00135 left += parag->counterWidth();
00136 }
00137 return left;
00138 }
00139
00140 int KoTextFormatterCore::rightMargin( bool firstLine ) const
00141 {
00142 int right = parag->rightMargin();
00143 if ( firstLine && parag->string()->isRightToLeft() )
00144 right += parag->firstLineMargin();
00145 return right;
00146 }
00147
00148 bool KoTextFormatterCore::format()
00149 {
00150 start = 0;
00151 KoTextString *string = parag->string();
00152 if ( start == 0 )
00153 c = &string->at( start );
00154 else
00155 c = 0;
00156
00157 KoTextStringChar *firstChar = 0;
00158 int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
00159 int initialLMargin = leftMargin( true );
00160
00161 y = doc && doc->addMargins() ? parag->topMargin() : 0;
00162
00163
00164
00165 if ( !parag->prev() )
00166 y = 0;
00167 else if ( parag->breakableTopMargin() )
00168 {
00169 int shift = doc->flow()->adjustFlow( parag->rect().y(),
00170 0 ,
00171 parag->breakableTopMargin() );
00172 if ( shift > 0 )
00173 {
00174
00175
00176 y = shift;
00177 }
00178
00179 }
00180
00181 y += parag->topMargin() - parag->breakableTopMargin();
00182 int len = parag->length();
00183
00184 int initialHeight = c->height();
00185
00186 int currentRightMargin = rightMargin( true );
00187 int initialRMargin = currentRightMargin;
00188
00189 i = start;
00190 parag->tabCache().clear();
00191 x = 0;
00192
00193
00194
00195
00196
00197 QPair<int, int> widths = determineCharWidth();
00198 int ww = widths.first;
00199 #ifndef REF_IS_LU
00200 int pixelww = widths.second;
00201 #endif
00202
00203
00204
00205 int dw = 0;
00206
00207 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00208 ww, initialLMargin, initialRMargin, dw,
00209 parag );
00210
00211
00212 x = initialLMargin;
00213
00214 int maxY = doc ? doc->flow()->availableHeight() : -1;
00215
00216 availableWidth = dw - initialRMargin;
00217 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH)
00218 kdDebug(32500) << "KoTextFormatter::format formatting parag " << parag->paragId()
00219 << " text:" << parag->string()->toString() << "\n"
00220 << " left=" << left << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " availableWidth=" << availableWidth << " maxY=" << maxY << endl;
00221 #else
00222 if ( availableWidth == 0 )
00223 kdDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, availableWidth=0" << endl;
00224 if ( maxY == 0 )
00225 kdDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, maxY=0" << endl;
00226 #endif
00227 bool fullWidth = TRUE;
00228
00229
00230
00231
00232
00233
00234
00235 wused = 0;
00236
00237 QValueList<TemporaryWordData> tempWordData;
00238
00239 #ifdef DEBUG_FORMATTER
00240 kdDebug(32500) << "Initial KoTextParagLineStart at y=" << y << endl;
00241 #endif
00242 KoTextParagLineStart *lineStart = new KoTextParagLineStart( y, 0, 0 );
00243 parag->insertLineStart( 0, lineStart );
00244 int lastBreak = -1;
00245
00246
00247 int tmpBaseLine = 0, tmph = 0;
00248
00249 int tmpWused = 0;
00250 bool lastWasNonInlineCustom = FALSE;
00251 bool abort = false;
00252
00253 int align = parag->alignment();
00254 if ( align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto )
00255 align = doc->alignment();
00256
00257 int col = 0;
00258
00259 maxAvailableWidth = qMakePair( 0, 0 );
00260
00261 KoZoomHandler *zh = doc->formattingZoomHandler();
00262 int pixelx = zh->layoutUnitToPixelX( x );
00263 int lastPixelx = 0;
00264
00265 KoTextStringChar* lastChr = 0;
00266 for ( ; i < len; ++i, ++col ) {
00267 if ( c )
00268 lastChr = c;
00269 c = &string->at( i );
00270 if ( i > 0 && (x > initialLMargin || ww == 0) || lastWasNonInlineCustom ) {
00271 c->lineStart = 0;
00272 } else {
00273 c->lineStart = 1;
00274 firstChar = c;
00275 tmph = c->height();
00276 tmpBaseLine = c->ascent();
00277 #ifdef DEBUG_FORMATTER_VERT
00278 kdDebug(32500) << "New line, initializing tmpBaseLine=" << tmpBaseLine << " tmph=" << tmph << endl;
00279 #endif
00280 }
00281
00282 if ( c->isCustom() && c->customItem()->placement() != KoTextCustomItem::PlaceInline )
00283 lastWasNonInlineCustom = TRUE;
00284 else
00285 lastWasNonInlineCustom = FALSE;
00286
00287 QPair<int, int> widths = determineCharWidth();
00288 ww = widths.first;
00289 pixelww = widths.second;
00290
00291
00292
00293
00294 if ( abort ) {
00295 x += ww;
00296 c->x = x;
00297 continue;
00298 }
00299
00300
00301 if ( c->isCustom() && c->customItem()->ownLine() ) {
00302 #ifdef DEBUG_FORMATTER
00303 kdDebug(32500) << "i=" << i << "/" << len << " custom item with ownline" << endl;
00304 #endif
00305 int rightMargin = currentRightMargin;
00306 x = left;
00307 if ( doc )
00308 doc->flow()->adjustMargins( y + parag->rect().y(), parag->rect().height(), 15,
00309 x, rightMargin, dw, parag );
00310 int w = dw - rightMargin;
00311 c->customItem()->resize( w - x );
00312 y += lineStart->h;
00313 lineStart = new KoTextParagLineStart( y, c->ascent(), c->height() );
00314
00315 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00316 lineStart->h += lineStart->lineSpacing;
00317 lineStart->w = dw;
00318 parag->insertLineStart( i, lineStart );
00319 tempWordData.clear();
00320 c->lineStart = 1;
00321 firstChar = c;
00322 x = 0xffffff;
00323
00324 continue;
00325 }
00326
00327 #ifdef DEBUG_FORMATTER
00328 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;
00329 #endif
00330
00331 if (
00332
00333 ( x + ww > availableWidth &&
00334 ( lastBreak != -1 || settings->allowBreakInWords() ) )
00335
00336
00337 && ( !settings->isBreakable( string, i ) ||
00338 ( i > 1 && lastBreak == i-1 && settings->isBreakable( string, i-2 ) ) ||
00339 lastBreak == -2 )
00340
00341
00342 && ( i < len-1 )
00343
00344
00345
00346
00347
00348
00350
00351
00352 || lastChr->c == '\n' && parag->isNewLinesAllowed() && lastBreak > -1 )
00353 {
00354 #ifdef DEBUG_FORMATTER
00355 kdDebug(32500) << "BREAKING" << endl;
00356 #endif
00357
00358
00359
00360 bool hyphenated = false;
00361
00362 if ( settings->hyphenator() && !c->isCustom() )
00363 {
00364 int wordStart = QMAX(0, lastBreak+1);
00365
00366 int maxlen = i - wordStart;
00367 QString word = string->mid( wordStart, maxlen );
00368 int wordEnd = i;
00369
00370 while ( wordEnd < len && !settings->isBreakable( string, wordEnd ) ) {
00371 word += string->at(wordEnd).c;
00372 wordEnd++;
00373 }
00374 if ( word.length() > 1 )
00375 {
00376 QString lang = string->at(wordStart).format()->language();
00377 char * hyphens = settings->hyphenator()->hyphens( word, lang );
00378 #if defined(DEBUG_HYPHENATION)
00379 kdDebug(32500) << "Hyphenation: word=" << word << " lang=" << lang << " hyphens=" << hyphens << " maxlen=" << maxlen << endl;
00380 kdDebug(32500) << "Parag indexes: wordStart=" << wordStart << " lastBreak=" << lastBreak << " i=" << i << endl;
00381 #endif
00382 int hylen = strlen(hyphens);
00383 Q_ASSERT( maxlen <= hylen );
00384
00385
00386 int minPos = QMAX( 0, (firstChar - &string->at(0)) - wordStart );
00387
00388
00389 for ( int hypos = maxlen-1 ; hypos >= minPos ; --hypos )
00390 if ( ( hyphens[hypos] % 2 )
00391 && string->at(hypos + wordStart).format()->hyphenation() )
00392 {
00393 lineStart->hyphenated = true;
00394 lastBreak = hypos + wordStart;
00395 hyphenated = true;
00396 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH) || defined(DEBUG_HYPHENATION)
00397 kdDebug(32500) << "Hyphenation: will break at " << lastBreak << " using tempworddata at position " << hypos << "/" << tempWordData.size() << endl;
00398 #endif
00399 if ( hypos < (int)tempWordData.size() )
00400 {
00401 const TemporaryWordData& twd = tempWordData[ hypos ];
00402 lineStart->baseLine = twd.baseLine;
00403 lineStart->h = twd.height;
00404 tmpWused = twd.lineWidth;
00405 }
00406 break;
00407 }
00408 delete[] hyphens;
00409 }
00410 }
00411
00412
00413 if ( lastBreak < 0 ) {
00414
00415
00416 const bool emptyLine = c->lineStart;
00417 if ( emptyLine )
00418 {
00419
00420
00421
00422
00423
00424
00425
00426 if ( availableWidth > maxAvailableWidth.second )
00427 {
00428 maxAvailableWidth.first = y;
00429 maxAvailableWidth.second = availableWidth;
00430 }
00431
00432
00433 if ( maxY > -1 && parag->rect().y() + y >= maxY )
00434 {
00435
00436
00437
00438
00439
00440 if ( c->width >= doc->flow()->width() )
00441 {
00442
00443 kdDebug(32500) << parag->rect().y() + y << " over maxY=" << maxY
00444 << " -> final choice for the line: y=" << maxAvailableWidth.first << endl;
00445 y = maxAvailableWidth.first;
00446 if ( availableWidth )
00447 Q_ASSERT( maxAvailableWidth.second != 0 );
00448 lineStart->y = y;
00449 maxAvailableWidth = qMakePair( 0, 0 );
00450 }
00451 else
00452 {
00453
00454 #ifdef DEBUG_FORMATTER
00455 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00456 #endif
00457
00458 abort = true;
00459 }
00460 }
00461 else
00462 {
00463
00464
00465
00466 y += tmph;
00467 kdDebug(32500) << "KoTextFormatter: moving down empty line by h=" << tmph << ": y=" << y << endl;
00468
00469 --i;
00470 continue;
00471 }
00472 }
00473 if ( !emptyLine && i > 0 )
00474 {
00475
00476 int belowBaseLine = QMAX( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00477 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
00478 lineStart->h = lineStart->baseLine + belowBaseLine;
00479 lineStart->w = dw;
00480
00481 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c-1, align, availableWidth - x );
00482 lineStart->lineSpacing = parag->lineSpacing( (int)parag->lineStartList().count()-1 );
00483 lineStart->h += lineStart->lineSpacing;
00484 y += lineStart->h;
00485 lineStart = lineStart2;
00486 #ifdef DEBUG_FORMATTER
00487 int linenr = parag->lineStartList().count()-1;
00488 kdDebug(32500) << "line " << linenr << " done (breaking at current char). y now " << y << endl;
00489 #endif
00490 tmph = c->height();
00491
00492 initialRMargin = currentRightMargin;
00493 x = left;
00494 if ( doc )
00495 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00496 ww,
00497 x, initialRMargin, dw, parag );
00498
00499 pixelx = zh->layoutUnitToPixelX( x );
00500 initialHeight = tmph;
00501 initialLMargin = x;
00502 availableWidth = dw - initialRMargin;
00503 if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
00504 int nx = parag->nextTab( i, x );
00505 if ( nx < x )
00506 ww = availableWidth - x;
00507 else
00508 ww = nx - x;
00509 }
00510 if ( x != left || availableWidth != dw )
00511 fullWidth = FALSE;
00512 lineStart->y = y;
00513 parag->insertLineStart( i, lineStart );
00514 tempWordData.clear();
00515 lineStart->baseLine = c->ascent();
00516 lineStart->h = c->height();
00517 c->lineStart = 1;
00518 firstChar = c;
00519 tmpBaseLine = lineStart->baseLine;
00520 lastBreak = -1;
00521 col = 0;
00522
00523 tmpWused = 0;
00524 }
00525
00526
00527
00528
00529 if ( !emptyLine && maxY > -1 )
00530 {
00531 if ( parag->rect().y() + y < maxY )
00532 {
00533 #ifdef DEBUG_FORMATTER
00534 kdDebug(32500) << "Re-checking formatting for character " << i << endl;
00535 #endif
00536 --i;
00537 continue;
00538 }
00539 else
00540 {
00541 #ifdef DEBUG_FORMATTER
00542 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00543 #endif
00544
00545 abort = true;
00546 }
00547 }
00548
00549
00550 } else {
00551
00552
00553 if ( maxY > -1 && parag->rect().y() + y + lineStart->h >= maxY ) {
00554 #ifdef DEBUG_FORMATTER
00555 kdDebug(32500) << "We're after maxY, time to stop." << endl;
00556 #endif
00557 abort = true;
00558 }
00559 else
00560 {
00561
00562 i = lastBreak;
00563 c = &string->at( i );
00564 int spaceAfterLine = availableWidth - c->x;
00565
00566
00567 spaceAfterLine -= c->width;
00568
00569
00570 if ( c->c.unicode() == 0xad || hyphenated )
00571 {
00572
00573 int width = KoTextZoomHandler::ptToLayoutUnitPt( c->format()->refFontMetrics().width( QChar(0xad) ) );
00574 if ( c->c.unicode() == 0xad )
00575 c->width = width;
00576 spaceAfterLine -= width;
00577 }
00578 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, spaceAfterLine );
00579 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00580 lineStart->w = dw;
00581 lineStart->h += lineStart->lineSpacing;
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 {
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 lineStart->lineSpacing = doc ? parag->lineSpacing( (int)parag->lineStartList().count()-1 ) : 0;
00794 lineStart->h += lineStart->lineSpacing;
00795 delete lineStart2;
00796 }
00797
00798
00799 wused = QMAX( wused, tmpWused );
00800 #ifdef DEBUG_FORMATTER_WIDTH
00801 kdDebug(32500) << "Done, wused=" << wused << endl;
00802 #endif
00803
00804 int m = parag->bottomMargin();
00805
00806
00807
00808 parag->setFullWidth( fullWidth );
00809
00810
00811 #ifdef DEBUG_FORMATTER_VERT
00812 kdDebug(32500) << "Adding height of last line(" << lineStart->h << ") and bottomMargin(" << m << ") to y(" << y << ") => " << y+lineStart->h+m << endl;
00813 #endif
00814 y += lineStart->h + m;
00815
00816 tmpWused += currentRightMargin;
00817
00818
00819
00820
00821 #ifdef DEBUG_FORMATTER
00822
00823 int numberOfLines = 0;
00824 QString charPosList;
00825 for ( int i = 0 ; i < len; ++i ) {
00826 KoTextStringChar *chr = &string->at( i );
00827 if ( i == 0 )
00828 assert( chr->lineStart );
00829 if ( chr->lineStart ) {
00830 ++numberOfLines;
00831 charPosList += QString::number(i) + " ";
00832 }
00833 }
00834 kdDebug(32500) << parag->lineStartList().count() << " lines. " << numberOfLines << " chars with lineStart set: " << charPosList << endl;
00835 assert( numberOfLines == (int)parag->lineStartList().count() );
00836 #endif
00837 return !abort;
00838 }
00839
00840
00841 void KoTextFormatterCore::moveChar( KoTextStringChar& chr, KoZoomHandler *zh,
00842 int deltaX, int deltaPixelX )
00843 {
00844 #ifndef REF_IS_LU
00845 int pixelx = chr.pixelxadj + zh->layoutUnitToPixelX( chr.x );
00846 #endif
00847 chr.x += deltaX;
00848 #ifndef REF_IS_LU
00849 chr.pixelxadj = pixelx + deltaPixelX - zh->layoutUnitToPixelX( chr.x );
00850 #endif
00851 }
00852
00853 KoTextParagLineStart *KoTextFormatterCore::koFormatLine(
00854 KoZoomHandler *zh,
00855 KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
00856 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00857 {
00858 if( string->isBidi() )
00859 return koBidiReorderLine( zh, parag, string, line, startChar, lastChar, align, space );
00860 int start = (startChar - &string->at(0));
00861 int last = (lastChar - &string->at(0) );
00862
00863 #if 0 // strange Qt code, breaks right-alignment
00864 KoTextStringChar *ch = lastChar;
00865 while ( ch > startChar && ch->whiteSpace ) {
00866 space += ch->format()->width( ' ' );
00867 --ch;
00868 }
00869 #endif
00870
00871 if (space < 0)
00872 space = 0;
00873
00874
00875 if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
00876 if ( align & Qt::AlignHCenter )
00877 space /= 2;
00878 int toAddPix = zh->layoutUnitToPixelX( space );
00879 for ( int j = last; j >= start; --j ) {
00880 KoTextStringChar &chr = string->at( j );
00882 if ( chr.c == '\t' ) {
00883 break;
00884 }
00885 moveChar( chr, zh, space, toAddPix );
00886 }
00887 } else if ( align & Qt::AlignJustify ) {
00888 int numSpaces = 0;
00889
00890 for ( int j = last-1; j >= start; --j ) {
00892 if ( string->at( j ).c == '\t' ) {
00893 start = j+1;
00894 break;
00895 }
00896 if( settings->isStretchable( string, j ) ) {
00897 numSpaces++;
00898 }
00899 }
00900 int toAdd = 0;
00901 int toAddPix = 0;
00902 for ( int k = start + 1; k <= last; ++k ) {
00903 KoTextStringChar &chr = string->at( k );
00904 if ( toAdd != 0 )
00905 moveChar( chr, zh, toAdd, toAddPix );
00906 if( settings->isStretchable( string, k ) && numSpaces ) {
00907 int s = space / numSpaces;
00908 toAdd += s;
00909 toAddPix = zh->layoutUnitToPixelX( toAdd );
00910 space -= s;
00911 numSpaces--;
00912 chr.width += s;
00913 #ifndef REF_IS_LU
00914 chr.pixelwidth += zh->layoutUnitToPixelX( s );
00915 #endif
00916 }
00917 }
00918 }
00919 int current=0;
00920 int nc=0;
00921 KoTextFormat refFormat( *string->at(0).format() );
00922 for(int i=start;i<=last;++i)
00923 {
00924 KoTextFormat* format=string->at(i).format();
00925
00926 if ( (((!format->underline())&&
00927 (!format->doubleUnderline())&&
00928 (!format->waveUnderline())&&
00929 (format->underlineType()!=KoTextFormat::U_SIMPLE_BOLD))
00930 || i == last)
00931 && nc )
00932 {
00933 double avg=static_cast<double>(current)/nc;
00934 avg/=18.0;
00935
00936 refFormat.setUnderLineWidth( avg );
00937 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth );
00938 nc=0;
00939 current=0;
00940 }
00941
00942 else if(format->underline()||
00943 format->waveUnderline()||
00944 format->doubleUnderline()||
00945 (format->underlineType() == KoTextFormat::U_SIMPLE_BOLD))
00946 {
00947 ++nc;
00948 current += format->pointSize();
00949 }
00950 }
00951 #if 0
00952 if ( last >= 0 && last < string->length() ) {
00953 KoTextStringChar &chr = string->at( last );
00954 line->w = chr.x + chr.width;
00955
00956 if ( line->hyphenated )
00957 line->w += KoTextZoomHandler::ptToLayoutUnitPt( chr.format()->refFontMetrics().width( QChar(0xad) ) );
00958 } else
00959 line->w = 0;
00960 #endif
00961
00962 return new KoTextParagLineStart();
00963 }
00964
00965
00966 KoTextParagLineStart *KoTextFormatterCore::koBidiReorderLine(
00967 KoZoomHandler *zh,
00968 KoTextParag * , KoTextString *text, KoTextParagLineStart *line,
00969 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00970 {
00971
00972
00973 #if 0
00974
00975 int endSpaces = 0;
00976 while ( lastChar > startChar && lastChar->whiteSpace ) {
00977 space += lastChar->format()->width( ' ' );
00978 --lastChar;
00979 ++endSpaces;
00980 }
00981 #endif
00982
00983 int start = (startChar - &text->at(0));
00984 int last = (lastChar - &text->at(0) );
00985 #ifdef DEBUG_FORMATTER
00986 kdDebug(32500) << "*KoTextFormatter::koBidiReorderLine from " << start << " to " << last << " space=" << space << " startChar->x=" << startChar->x << endl;
00987 #endif
00988 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
00989 QString str;
00990 str.setUnicode( 0, last - start + 1 );
00991
00992 KoTextStringChar *ch = startChar;
00993 QChar *qch = (QChar *)str.unicode();
00994 while ( ch <= lastChar ) {
00995 *qch = ch->c;
00996 qch++;
00997 ch++;
00998 }
00999 int x = startChar->x;
01000
01001 QPtrList<KoTextRun> *runs;
01002 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01003 (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01004
01005
01006
01007 int numSpaces = 0;
01008
01009 if( align == Qt::AlignAuto ) {
01010
01011 if ( text->isRightToLeft() )
01012 align = Qt::AlignRight;
01013 }
01014
01015 if ( align & Qt::AlignHCenter ) {
01016 x += space/2;
01017 } else if ( align & Qt::AlignRight ) {
01018 x += space;
01019 } else if ( align & Qt::AlignJustify ) {
01020 for ( int j = last - 1; j >= start; --j ) {
01022 if ( text->at( j ).c == '\t' ) {
01023 start = j+1;
01024 break;
01025 }
01026 if( settings->isStretchable( text, j ) ) {
01027 numSpaces++;
01028 }
01029 }
01030 }
01031
01032 int pixelx = zh->layoutUnitToPixelX( x );
01033 int toAdd = 0;
01034 int toAddPix = 0;
01035 bool first = TRUE;
01036 KoTextRun *r = runs->first();
01037 int xmax = -0xffffff;
01038 while ( r ) {
01039 #ifdef DEBUG_FORMATTER
01040 kdDebug(32500) << "koBidiReorderLine level: " << r->level << endl;
01041 #endif
01042 if(r->level %2) {
01043
01044 int pos = r->stop + start;
01045 while(pos >= r->start + start) {
01046 KoTextStringChar &chr = text->at(pos);
01047 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01048 int s = space / numSpaces;
01049 toAdd += s;
01050 toAddPix = zh->layoutUnitToPixelX( toAdd );
01051 space -= s;
01052 numSpaces--;
01053 chr.width += s;
01054 chr.pixelwidth += zh->layoutUnitToPixelX( s );
01055 } else if ( first ) {
01056 first = FALSE;
01057 if ( chr.c == ' ' )
01058 {
01059
01060 x -= chr.width;
01061 pixelx -= chr.pixelwidth;
01062 }
01063 }
01064 chr.x = x + toAdd;
01065 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01066 #ifdef DEBUG_FORMATTER
01067 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;
01068 #endif
01069 chr.rightToLeft = TRUE;
01070 chr.startOfRun = FALSE;
01071 int ww = chr.width;
01072 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01073 x += ww;
01074 pixelx += chr.pixelwidth;
01075 #ifdef DEBUG_FORMATTER
01076 kdDebug(32500) << " ww=" << ww << " adding to x, now " << x << ". pixelwidth=" << chr.pixelwidth << " adding to pixelx, now " << pixelx << " xmax=" << xmax << endl;
01077 #endif
01078 pos--;
01079 }
01080 } else {
01081 int pos = r->start + start;
01082 while(pos <= r->stop + start) {
01083 KoTextStringChar& chr = text->at(pos);
01084 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01085 int s = space / numSpaces;
01086 toAdd += s;
01087 toAddPix = zh->layoutUnitToPixelX( toAdd );
01088 space -= s;
01089 numSpaces--;
01090 } else if ( first ) {
01091 first = FALSE;
01092 if ( chr.c == ' ' )
01093 {
01094
01095 x -= chr.width;
01096 pixelx -= chr.pixelwidth;
01097 }
01098 }
01099 chr.x = x + toAdd;
01100 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01101 chr.rightToLeft = FALSE;
01102 chr.startOfRun = FALSE;
01103 int ww = chr.width;
01104
01105 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01106 x += ww;
01107 pixelx += chr.pixelwidth;
01108 pos++;
01109 }
01110 }
01111 text->at( r->start + start ).startOfRun = TRUE;
01112 r = runs->next();
01113 }
01114
01115
01116 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01117 delete control;
01118 delete runs;
01119 return ls;
01120 }
01121
01122 void KoTextFormatter::postFormat( KoTextParag* parag )
01123 {
01124 parag->fixParagWidth( viewFormattingChars() );
01125 }