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