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 #include <Q3ValueList>
00032 #include <Q3PtrList>
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00046
00047
00048 KoTextFormatter::KoTextFormatter()
00049 {
00050 try {
00051 m_hyphenator = KoHyphenator::self();
00052 } catch ( KoHyphenatorException& e )
00053 {
00054 m_hyphenator = 0L;
00055 }
00056 }
00057
00058 KoTextFormatter::~KoTextFormatter()
00059 {
00060 }
00061
00062
00063
00064 struct TemporaryWordData
00065 {
00066 int baseLine;
00067 int height;
00068 int lineWidth;
00069 };
00070
00071 bool KoTextFormatter::format( KoTextDocument *doc, KoTextParag *parag,
00072 int start, const QMap<int, KoTextParagLineStart*> &,
00073 int& y, int& widthUsed )
00074 {
00075 KoTextFormatterCore formatter( this, doc, parag, start );
00076 bool worked = formatter.format();
00077 y = formatter.resultY();
00078 widthUsed = formatter.widthUsed();
00079 return worked;
00080 }
00081
00082 KoTextFormatterCore::KoTextFormatterCore( KoTextFormatter* _settings,
00083 KoTextDocument *_doc, KoTextParag *_parag,
00084 int _start )
00085 : settings(_settings), doc(_doc), parag(_parag), start(_start)
00086 {
00087 }
00088
00089 QPair<int, int> KoTextFormatterCore::determineCharWidth()
00090 {
00091 int ww, pixelww;
00092 KoTextZoomHandler *zh = doc->formattingZoomHandler();
00093 if ( c->c != '\t' || c->isCustom() ) {
00094 KoTextFormat *charFormat = c->format();
00095 if ( c->isCustom() ) {
00096 ww = c->customItem()->width;
00097 Q_ASSERT( ww >= 0 );
00098 ww = qMax(0, ww);
00099 #ifndef REF_IS_LU
00100 pixelww = zh->layoutUnitToPixelX( ww );
00101 #endif
00102 } else {
00103 ww = charFormat->charWidthLU( c, parag, i );
00104 #ifndef REF_IS_LU
00105
00106 pixelww = charFormat->charWidth( zh, true, c, parag, i );
00107 #endif
00108 }
00109 } else {
00110 int nx = parag->nextTab( i, x, availableWidth );
00111 if ( nx < x )
00112 ww = availableWidth - x;
00113 else
00114 ww = nx - x;
00115 #ifdef DEBUG_FORMATTER
00116 kDebug(32500) << "nextTab for x=" << x << " returned nx=" << nx << " (=> ww=" << ww << ")" << endl;
00117 #endif
00118 #ifndef REF_IS_LU
00119 pixelww = zh->layoutUnitToPixelX( ww );
00120 #endif
00121 }
00122 Q_ASSERT( ww >= 0 );
00123 c->width = ww;
00124 return qMakePair(ww, pixelww);
00125 }
00126
00127
00128 int KoTextFormatterCore::leftMargin( bool firstLine, bool includeFirstLineMargin ) const
00129 {
00130 int left = parag->leftMargin() + doc->leftMargin();
00131 if ( firstLine && !parag->string()->isRightToLeft() )
00132 {
00133 if ( includeFirstLineMargin )
00134 left += parag->firstLineMargin();
00135
00136 if( parag->counter() &&
00137 ( parag->counter()->alignment() == Qt::AlignLeft ||
00138 parag->counter()->alignment() == Qt::AlignLeft ) )
00139 left += parag->counterWidth();
00140 }
00141 return left;
00142 }
00143
00144 int KoTextFormatterCore::rightMargin( bool firstLine ) const
00145 {
00146 int right = parag->rightMargin();
00147 if ( firstLine && parag->string()->isRightToLeft() )
00148 right += parag->firstLineMargin();
00149 return right;
00150 }
00151
00152 bool KoTextFormatterCore::format()
00153 {
00154 start = 0;
00155 KoTextString *string = parag->string();
00156 if ( start == 0 )
00157 c = &string->at( start );
00158 else
00159 c = 0;
00160
00161 KoTextStringChar *firstChar = 0;
00162 int left = leftMargin( true, false );
00163 int initialLMargin = leftMargin( true );
00164
00165 y = parag->breakableTopMargin();
00166
00167
00168
00169 if ( !parag->prev() )
00170 y = 0;
00171 else if ( y )
00172 {
00173 int shift = doc->flow()->adjustFlow( parag->rect().y(),
00174 0 ,
00175 y );
00176 if ( shift > 0 )
00177 {
00178
00179
00180 y = shift;
00181 }
00182
00183 }
00184
00185 y += parag->topMargin() - parag->breakableTopMargin();
00186 int len = parag->length();
00187
00188 int initialHeight = c->height();
00189
00190 int currentRightMargin = rightMargin( true );
00191 int initialRMargin = currentRightMargin;
00192
00193 i = start;
00194 parag->tabCache().clear();
00195 x = 0;
00196
00197
00198
00199
00200
00201 QPair<int, int> widths = determineCharWidth();
00202 int ww = widths.first;
00203 #ifndef REF_IS_LU
00204 int pixelww = widths.second;
00205 #endif
00206
00207
00208
00209 int dw = 0;
00210 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00211 ww, initialLMargin, initialRMargin, dw,
00212 parag );
00213
00214
00215 x = initialLMargin;
00216
00217 int maxY = doc->flow()->availableHeight();
00218
00219 availableWidth = dw - initialRMargin;
00220 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH)
00221 kDebug(32500) << "KoTextFormatter::format formatting parag " << parag->paragId()
00222 << " text:" << parag->string()->toString() << "\n"
00223 << " left=" << left << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " availableWidth=" << availableWidth << " maxY=" << maxY << endl;
00224 #else
00225 if ( availableWidth == 0 )
00226 kDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, availableWidth=0" << endl;
00227 if ( maxY == 0 )
00228 kDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, maxY=0" << endl;
00229 #endif
00230 bool fullWidth = true;
00231
00232
00233
00234
00235
00236
00237
00238 wused = 0;
00239
00240 Q3ValueList<TemporaryWordData> tempWordData;
00241
00242 #ifdef DEBUG_FORMATTER
00243 kDebug(32500) << "Initial KoTextParagLineStart at y=" << y << endl;
00244 #endif
00245 KoTextParagLineStart *lineStart = new KoTextParagLineStart( y, 0, 0 );
00246 parag->insertLineStart( 0, lineStart );
00247 int lastBreak = -1;
00248
00249
00250 int tmpBaseLine = 0, tmph = 0;
00251
00252 int tmpWused = 0;
00253 bool lastWasNonInlineCustom = false;
00254 bool abort = false;
00255
00256 int align = parag->alignment();
00257 if ( align == Qt::AlignLeft && doc->alignment() != Qt::AlignLeft )
00258 align = doc->alignment();
00259
00260 int col = 0;
00261
00262 maxAvailableWidth = qMakePair( 0, 0 );
00263
00264 KoTextZoomHandler *zh = doc->formattingZoomHandler();
00265 int pixelx = zh->layoutUnitToPixelX( x );
00266 int lastPixelx = 0;
00267
00268 KoTextStringChar* lastChr = 0;
00269 for ( ; i < len; ++i, ++col ) {
00270 if ( c )
00271 lastChr = c;
00272 c = &string->at( i );
00273 if ( i > 0 && (x > initialLMargin || ww == 0) || lastWasNonInlineCustom ) {
00274 c->lineStart = 0;
00275 } else {
00276 c->lineStart = 1;
00277 firstChar = c;
00278 tmph = c->height();
00279 tmpBaseLine = c->ascent();
00280 #ifdef DEBUG_FORMATTER_VERT
00281 kDebug(32500) << "New line, initializing tmpBaseLine=" << tmpBaseLine << " tmph=" << tmph << endl;
00282 #endif
00283 }
00284
00285 if ( c->isCustom() && c->customItem()->placement() != KoTextCustomItem::PlaceInline )
00286 lastWasNonInlineCustom = true;
00287 else
00288 lastWasNonInlineCustom = false;
00289
00290 QPair<int, int> widths = determineCharWidth();
00291 ww = widths.first;
00292 pixelww = widths.second;
00293
00294
00295
00296
00297 if ( abort ) {
00298 x += ww;
00299 c->x = x;
00300 continue;
00301 }
00302
00303
00304 if ( c->isCustom() && c->customItem()->ownLine() ) {
00305 #ifdef DEBUG_FORMATTER
00306 kDebug(32500) << "i=" << i << "/" << len << " custom item with ownline" << endl;
00307 #endif
00308 int rightMargin = currentRightMargin;
00309 x = left;
00310 doc->flow()->adjustMargins( y + parag->rect().y(), parag->rect().height(), 15,
00311 x, rightMargin, dw, parag );
00312 int w = dw - rightMargin;
00313 c->customItem()->resize( w - x );
00314 y += lineStart->h;
00315 lineStart = new KoTextParagLineStart( y, c->ascent(), c->height() );
00316
00317 lineStart->lineSpacing = parag->calculateLineSpacing( (int)parag->lineStartList().count()-1, i, i );
00318 lineStart->h += lineStart->lineSpacing;
00319 lineStart->w = dw;
00320 parag->insertLineStart( i, lineStart );
00321 tempWordData.clear();
00322 c->lineStart = 1;
00323 firstChar = c;
00324 x = 0xffffff;
00325
00326 continue;
00327 }
00328
00329 #ifdef DEBUG_FORMATTER
00330 kDebug(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;
00331 #endif
00332
00333 if (
00334
00335 ( x + ww > availableWidth &&
00336 ( lastBreak != -1 || settings->allowBreakInWords() ) )
00337
00338
00339 && ( !settings->isBreakable( string, i ) ||
00340 ( i > 1 && lastBreak == i-1 && settings->isBreakable( string, i-2 ) ) ||
00341 lastBreak == -2 )
00342
00343
00344 && ( i < len-1 )
00345
00346
00347
00348
00349
00350
00352
00353
00354 || lastChr && lastChr->c == '\n' && parag->isNewLinesAllowed() && lastBreak > -1 )
00355 {
00356 #ifdef DEBUG_FORMATTER
00357 kDebug(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 kDebug(32500) << "Hyphenation: word=" << word << " lang=" << lang << " hyphens=" << hyphens << " maxlen=" << maxlen << endl;
00382 kDebug(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, int(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 kDebug(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 kDebug(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 kDebug(32500) << "Line has a height of " << tmph << ", let's stop." << endl;
00459 else
00460 kDebug(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 kDebug(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( (int)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 kDebug(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 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 kDebug(32500) << "Re-checking formatting for character " << i << endl;
00537 #endif
00538 --i;
00539 continue;
00540 }
00541 else
00542 {
00543 #ifdef DEBUG_FORMATTER
00544 kDebug(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 kDebug(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 kDebug(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 kDebug(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 doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00597 c->width,
00598 x, initialRMargin, dw, parag );
00599
00600 pixelx = zh->layoutUnitToPixelX( x );
00601 initialHeight = tmph;
00602 initialLMargin = x;
00603 availableWidth = dw - initialRMargin;
00604 if ( x != left || availableWidth != dw )
00605 fullWidth = false;
00606 lineStart->y = y;
00607 parag->insertLineStart( i + 1, lineStart );
00608 tempWordData.clear();
00609 lineStart->baseLine = c->ascent();
00610 lineStart->h = c->height();
00611 firstChar = c;
00612 tmpBaseLine = lineStart->baseLine;
00613 lastBreak = -1;
00614 col = 0;
00615
00616 tmpWused = 0;
00617 c->lineStart = 1;
00618 continue;
00619 }
00620 }
00621 } else if ( lineStart && ( settings->isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) {
00622
00623 if ( len <= 2 || i < len - 1 ) {
00624 #ifdef DEBUG_FORMATTER_VERT
00625 kDebug(32500) << " Breakable character (i=" << i << " len=" << len << "):"
00626 << " combining " << tmpBaseLine << "/" << tmph
00627 << " with " << c->ascent() << "/" << c->height() << endl;
00628 #endif
00629
00630 int belowBaseLine = qMax( tmph - tmpBaseLine, c->height() - c->ascent() );
00631 tmpBaseLine = qMax( tmpBaseLine, c->ascent() );
00632 tmph = tmpBaseLine + belowBaseLine;
00633 #ifdef DEBUG_FORMATTER_VERT
00634 kDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00635 #endif
00636 }
00637 tempWordData.clear();
00638
00639
00640 wused = qMax( wused, tmpWused );
00641 #ifdef DEBUG_FORMATTER_WIDTH
00642 kDebug(32500) << " Breakable character (i=" << i << " len=" << len << "): wused=" << wused << endl;
00643 #endif
00644 tmpWused = 0;
00645
00646 #ifdef DEBUG_FORMATTER_VERT
00647 kDebug(32500) << "Breakable character: combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00648 #endif
00649 int belowBaseLine = qMax( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00650 lineStart->baseLine = qMax( (int)lineStart->baseLine, tmpBaseLine );
00651 lineStart->h = lineStart->baseLine + belowBaseLine;
00652 lineStart->w = dw;
00653 #ifdef DEBUG_FORMATTER_VERT
00654 kDebug(32500) << " -> line baseLine/height : " << lineStart->baseLine << "/" << lineStart->h << endl;
00655 #endif
00656
00657
00658 if ( lineStart->h > initialHeight )
00659 {
00660 bool firstLine = ( firstChar == &string->at( 0 ) );
00661 int newLMargin = leftMargin( firstLine );
00662 int newRMargin = rightMargin( firstLine );
00663 int newPageWidth = dw;
00664 initialHeight = lineStart->h;
00665 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00666 firstChar->width,
00667 newLMargin, newRMargin, newPageWidth, parag );
00668
00669 #ifdef DEBUG_FORMATTER
00670 kDebug(32500) << "new height: " << initialHeight << " => left=" << left << " first-char=" << (firstChar==&string->at(0)) << " newLMargin=" << newLMargin << " newRMargin=" << newRMargin << endl;
00671 #endif
00672 if ( newLMargin != initialLMargin || newRMargin != initialRMargin || newPageWidth != dw )
00673 {
00674 #ifdef DEBUG_FORMATTER
00675 kDebug(32500) << "formatting again" << endl;
00676 #endif
00677 i = (firstChar - &string->at(0));
00678 x = newLMargin;
00679 pixelx = zh->layoutUnitToPixelX( x );
00680 availableWidth = dw - newRMargin;
00681 initialLMargin = newLMargin;
00682 initialRMargin = newRMargin;
00683 dw = newPageWidth;
00684 c = &string->at( i );
00685 tmph = c->height();
00686 tmpBaseLine = c->ascent();
00687 lineStart->h = tmph;
00688 lineStart->baseLine = tmpBaseLine;
00689 lastBreak = -1;
00690 col = 0;
00691
00692 #ifdef DEBUG_FORMATTER
00693 kDebug(32500) << "Restarting with i=" << i << " x=" << x << " y=" << y << " tmph=" << tmph << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " y=" << y << endl;
00694 #endif
00695
00696
00697 ww = c->width;
00698 #ifndef REF_IS_LU
00699 pixelww = c->pixelwidth;
00700 #endif
00701
00702 tmpWused = 0;
00703 }
00704 }
00705
00706
00707 if ( i < len - 2 || c->c != ' ' )
00708 lastBreak = i;
00709
00710 } else if ( i < len - 1 ) {
00711
00712
00713 #ifdef DEBUG_FORMATTER_VERT
00714 kDebug(32500) << " Non-breakable character: combining " << tmpBaseLine << "/" << tmph << " with " << c->ascent() << "/" << c->height() << endl;
00715 #endif
00716
00717 int belowBaseLine = qMax( tmph - tmpBaseLine, c->height() - c->ascent() );
00718 tmpBaseLine = qMax( tmpBaseLine, c->ascent() );
00719 tmph = tmpBaseLine + belowBaseLine;
00720 #ifdef DEBUG_FORMATTER_VERT
00721 kDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00722 #endif
00723
00724 TemporaryWordData twd;
00725 twd.baseLine = tmpBaseLine;
00726 twd.height = tmph;
00727 twd.lineWidth = tmpWused;
00728 tempWordData.append( twd );
00729 }
00730
00731 c->x = x;
00732
00733
00734 c->pixelxadj = pixelx - zh->layoutUnitToPixelX( x );
00735
00736 #ifdef DEBUG_FORMATTER
00737 kDebug(32500) << "LU: x=" << x << " [equiv. to pix=" << zh->layoutUnitToPixelX( x ) << "] ; PIX: x=" << pixelx << " --> adj=" << c->pixelxadj << endl;
00738 #endif
00739
00740 x += ww;
00741
00742 if ( i > 0 && lastChr )
00743 lastChr->pixelwidth = pixelx - lastPixelx;
00744 if ( i < len - 1 )
00745 tmpWused = qMax( tmpWused, x );
00746 else
00747 c->pixelwidth = zh->layoutUnitToPixelX( ww );
00748
00749 lastPixelx = pixelx;
00750 #ifdef REF_IS_LU
00751 pixelx = zh->layoutUnitToPixelX( x );
00752 #else
00753 pixelx += pixelww;
00754 #endif
00755 #ifdef DEBUG_FORMATTER
00756 kDebug(32500) << "LU: added " << ww << " -> now x=" << x << " ; PIX: added " << pixelww << " -> now pixelx=" << pixelx << endl;
00757 #endif
00758 }
00759
00760
00761
00762 if ( len > 1 ) {
00763 c->format()->removeRef();
00764 c->setFormat( string->at( len - 2 ).format() );
00765 c->format()->addRef();
00766 }
00767
00768
00769 if ( lineStart ) {
00770 #ifdef DEBUG_FORMATTER
00771 kDebug(32500) << "Last Line.... linenr=" << (int)parag->lineStartList().count()-1 << endl;
00772 #endif
00773 #ifdef DEBUG_FORMATTER_VERT
00774 kDebug(32500) << "Last Line... Combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00775 #endif
00776
00777 int belowBaseLine = qMax( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00778 lineStart->baseLine = qMax( (int)lineStart->baseLine, tmpBaseLine );
00779 lineStart->h = lineStart->baseLine + belowBaseLine;
00780 lineStart->w = dw;
00781 #ifdef DEBUG_FORMATTER_WIDTH
00782 kDebug(32500) << "Last line: w = dw = " << dw << endl;
00783 #endif
00784 #ifdef DEBUG_FORMATTER_VERT
00785 kDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl;
00786 #endif
00787
00788 if ( align == Qt::AlignJustify )
00789 align = Qt::AlignLeft;
00790 int space = availableWidth - x + c->width;
00791 KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, space );
00792 delete lineStart2;
00793 }
00794
00795
00796 wused = qMax( wused, tmpWused );
00797 #ifdef DEBUG_FORMATTER_WIDTH
00798 kDebug(32500) << "Done, wused=" << wused << endl;
00799 #endif
00800
00801 int m = parag->bottomMargin();
00802
00803
00804
00805 parag->setFullWidth( fullWidth );
00806
00807
00808 #ifdef DEBUG_FORMATTER_VERT
00809 kDebug(32500) << "Adding height of last line(" << lineStart->h << ") and bottomMargin(" << m << ") to y(" << y << ") => " << y+lineStart->h+m << endl;
00810 #endif
00811 y += lineStart->h + m;
00812
00813 tmpWused += currentRightMargin;
00814
00815
00816
00817
00818 #ifdef DEBUG_FORMATTER
00819
00820 int numberOfLines = 0;
00821 QString charPosList;
00822 for ( int i = 0 ; i < len; ++i ) {
00823 KoTextStringChar *chr = &string->at( i );
00824 if ( i == 0 )
00825 assert( chr->lineStart );
00826 if ( chr->lineStart ) {
00827 ++numberOfLines;
00828 charPosList += QString::number(i) + ' ';
00829 }
00830 }
00831 kDebug(32500) << parag->lineStartList().count() << " lines. " << numberOfLines << " chars with lineStart set: " << charPosList << endl;
00832 assert( numberOfLines == (int)parag->lineStartList().count() );
00833 #endif
00834 return !abort;
00835 }
00836
00837
00838 void KoTextFormatterCore::moveChar( KoTextStringChar& chr, KoTextZoomHandler *zh,
00839 int deltaX, int deltaPixelX )
00840 {
00841 #ifndef REF_IS_LU
00842 int pixelx = chr.pixelxadj + zh->layoutUnitToPixelX( chr.x );
00843 #endif
00844 chr.x += deltaX;
00845 #ifndef REF_IS_LU
00846 chr.pixelxadj = pixelx + deltaPixelX - zh->layoutUnitToPixelX( chr.x );
00847 #endif
00848 }
00849
00850 KoTextParagLineStart *KoTextFormatterCore::koFormatLine(
00851 KoTextZoomHandler *zh,
00852 KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
00853 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00854 {
00855 KoTextParagLineStart* ret = 0;
00856 #if 0 // TODO RTL SUPPORT
00857 if( string->isBidi() ) {
00858 ret = koBidiReorderLine( zh, parag, string, line, startChar, lastChar, align, space );
00859 } else
00860 #endif
00861 {
00862 int start = (startChar - &string->at(0));
00863 int last = (lastChar - &string->at(0) );
00864
00865 if (space < 0)
00866 space = 0;
00867
00868
00869 if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
00870 if ( align & Qt::AlignHCenter )
00871 space /= 2;
00872 int toAddPix = zh->layoutUnitToPixelX( space );
00873 for ( int j = last; j >= start; --j ) {
00874 KoTextStringChar &chr = string->at( j );
00875 moveChar( chr, zh, space, toAddPix );
00876 }
00877 } else if ( align & Qt::AlignJustify ) {
00878 int numSpaces = 0;
00879
00880 for ( int j = last-1; j >= start; --j ) {
00882 if ( string->at( j ).c == '\t' ) {
00883 start = j+1;
00884 break;
00885 }
00886 if( settings->isStretchable( string, j ) ) {
00887 numSpaces++;
00888 }
00889 }
00890 int toAdd = 0;
00891 int toAddPix = 0;
00892 for ( int k = start + 1; k <= last; ++k ) {
00893 KoTextStringChar &chr = string->at( k );
00894 if ( toAdd != 0 )
00895 moveChar( chr, zh, toAdd, toAddPix );
00896 if( settings->isStretchable( string, k ) && numSpaces ) {
00897 int s = space / numSpaces;
00898 toAdd += s;
00899 toAddPix = zh->layoutUnitToPixelX( toAdd );
00900 space -= s;
00901 numSpaces--;
00902 chr.width += s;
00903 #ifndef REF_IS_LU
00904 chr.pixelwidth += zh->layoutUnitToPixelX( s );
00905 #endif
00906 }
00907 }
00908 }
00909 int current=0;
00910 int nc=0;
00911 KoTextFormat refFormat( *string->at(0).format() );
00912 for(int i=start;i<=last;++i)
00913 {
00914 KoTextFormat* format=string->at(i).format();
00915
00916 if ( (((!format->underline())&&
00917 (!format->doubleUnderline())&&
00918 (!format->waveUnderline())&&
00919 (format->underlineType()!=KoTextFormat::U_SIMPLE_BOLD))
00920 || i == last)
00921 && nc )
00922 {
00923 double avg=static_cast<double>(current)/nc;
00924 avg/=18.0;
00925
00926 refFormat.setUnderLineWidth( avg );
00927 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth );
00928 nc=0;
00929 current=0;
00930 }
00931
00932 else if(format->underline()||
00933 format->waveUnderline()||
00934 format->doubleUnderline()||
00935 (format->underlineType() == KoTextFormat::U_SIMPLE_BOLD))
00936 {
00937 ++nc;
00938 current += format->pointSize();
00939 }
00940 }
00941 #if 0
00942 if ( last >= 0 && last < string->length() ) {
00943 KoTextStringChar &chr = string->at( last );
00944 line->w = chr.x + chr.width;
00945
00946 if ( line->hyphenated )
00947 line->w += KoTextZoomHandler::ptToLayoutUnitPt( chr.format()->refFontMetrics().width( QChar(0xad) ) );
00948 } else
00949 line->w = 0;
00950 #endif
00951
00952 ret = new KoTextParagLineStart();
00953 }
00954
00955
00956 const int start = (startChar - &string->at(0));
00957 const int last = (lastChar - &string->at(0) );
00958 line->lineSpacing = parag->calculateLineSpacing( (int)parag->lineStartList().count()-1, start, last );
00959 line->h += line->lineSpacing;
00960
00961 return ret;
00962 }
00963
00964
00965 #if 0
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 kDebug(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 Q3PtrList<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::AlignLeft ) {
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 kDebug(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 kDebug(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 kDebug(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 #endif
01123
01124 void KoTextFormatter::postFormat( KoTextParag* parag )
01125 {
01126 parag->fixParagWidth( viewFormattingChars() );
01127 }