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