lib Library API Documentation

kotextformatter.cc

00001 /* This file is part of the KDE project 00002 Copyright (C) 2001 David Faure <faure@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 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 //#define DEBUG_FORMATTER 00035 00036 // Vertical info (height, baseline etc.) 00037 //#define DEBUG_FORMATTER_VERT 00038 00039 // Line and paragraph width 00040 //#define DEBUG_FORMATTER_WIDTH 00041 00042 // Hyphenation 00043 //#define DEBUG_HYPHENATION 00044 00046 //#define REF_IS_LU 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 // Hyphenation can break anywhere in the word, so 00063 // remember the temp data for every char. 00064 struct TemporaryWordData 00065 { 00066 int baseLine; 00067 int height; 00068 int lineWidth; // value of wused 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 // Pixel size - we want the metrics of the font that's going to be used. 00106 pixelww = charFormat->charWidth( zh, true, c, parag, i ); 00107 #endif 00108 } 00109 } else { // tab 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 = /*doc ?*/ parag->leftMargin() + doc->leftMargin() /*: 0*/; 00128 if ( firstLine && !parag->string()->isRightToLeft() ) 00129 { 00130 left += parag->firstLineMargin(); 00131 // Add the width of the paragraph counter - first line of parag only. 00132 if( parag->counter() && 00133 ( parag->counter()->alignment() == Qt::AlignLeft || 00134 parag->counter()->alignment() == Qt::AlignAuto ) ) 00135 left += parag->counterWidth(); // in LU pixels 00136 } 00137 return left; 00138 } 00139 00140 int KoTextFormatterCore::rightMargin( bool firstLine ) const 00141 { 00142 int right = parag->rightMargin(); // 'rm' in QRT 00143 if ( /*doc &&*/ firstLine && parag->string()->isRightToLeft() ) 00144 right += parag->firstLineMargin(); 00145 return right; 00146 } 00147 00148 bool KoTextFormatterCore::format() 00149 { 00150 start = 0; // we don't do partial formatting yet 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 // #57555, top margin doesn't apply if parag at top of page 00163 // (but a portion of the margin can be needed, to complete the prev page) 00164 // So we apply formatVertically() on the top margin, to find where to break it. 00165 if ( !parag->prev() ) 00166 y = 0; // no top margin on very first parag 00167 else if ( parag->breakableTopMargin() ) 00168 { 00169 int shift = doc->flow()->adjustFlow( parag->rect().y(), 00170 0 /*w, unused*/, 00171 parag->breakableTopMargin() ); 00172 if ( shift > 0 ) 00173 { 00174 // The shift is in fact the amount of top-margin that should remain 00175 // The remaining portion should be eaten away. 00176 y = shift; 00177 } 00178 00179 } 00180 // Now add the rest of the top margin (e.g. the one for the border) 00181 y += parag->topMargin() - parag->breakableTopMargin(); 00182 int len = parag->length(); 00183 00184 int initialHeight = c->height(); // remember what adjustMargins was called with 00185 00186 int currentRightMargin = rightMargin( true ); 00187 int initialRMargin = currentRightMargin; 00188 // Those three things must be done before calling determineCharWidth 00189 i = start; 00190 parag->tabCache().clear(); 00191 x = 0; 00192 00193 // We need the width of the first char for adjustMargins 00194 // The result might not be 100% accurate when using a tab (it'll use x=0 00195 // but with counters/margins this might be different). This is why 00196 // we call determineCharWidth() again from within the loop. 00197 QPair<int, int> widths = determineCharWidth(); 00198 int ww = widths.first; // width in layout units 00199 #ifndef REF_IS_LU 00200 int pixelww = widths.second; // width in pixels 00201 #endif 00202 00203 // dw is the document width, i.e. the maximum available width, all included. 00204 // We are in a variable-width design, so it is returned by each call to adjustMargins. 00205 int dw = 0; 00206 //if (doc) // always true in kotext 00207 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight, // input params 00208 ww, initialLMargin, initialRMargin, dw, // output params 00209 parag ); 00210 //else dw = parag->documentVisibleWidth(); 00211 00212 x = initialLMargin; // as modified by adjustMargins 00213 00214 int maxY = doc ? doc->flow()->availableHeight() : -1; 00215 00216 availableWidth = dw - initialRMargin; // 'w' in QRT 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 //int marg = left + initialRMargin; 00229 00230 // minw is the really minimum width needed for this paragraph, i.e. 00231 // the width of the longest set of non-breakable characters together. 00232 // Currently unused. 00233 //int minw = 0; 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 // tmph, tmpBaseLine and tminw are used after the last breakable char 00247 // we don't know yet if we'll break there, or later. 00248 int tmpBaseLine = 0, tmph = 0; 00249 //int tminw = marg; 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 // We're "aborting" the formatting. This still means we need to set the 00293 // lineStart bools to false (trouble ahead, otherwise!), and while we're at 00294 // it we also calculate the widths etc. 00295 if ( abort ) { 00296 x += ww; 00297 c->x = x; 00298 continue; // yeah, this seems a bit confusing :) 00299 } 00300 00301 //code from qt-3.1beta2 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 // Added for kotext (to be tested) 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 // Hmm, --i or setting lineStart on next char too? 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 // Wrapping at end of line - one big if :) 00332 if ( wrapEnabled 00333 // Check if should break (i.e. we are after the max X for the end of the line) 00334 && ( /*wrapAtColumn() == -1 &&*/ x + ww > availableWidth && 00335 ( lastBreak != -1 || settings->allowBreakInWords() ) 00336 /*|| wrapAtColumn() != -1 && col >= wrapAtColumn()*/ ) 00337 00338 // Allow two breakable chars next to each other (e.g. ' ') but not more 00339 && ( !settings->isBreakable( string, i ) || 00340 ( i > 1 && lastBreak == i-1 && settings->isBreakable( string, i-2 ) ) || 00341 lastBreak == -2 ) // ... used to be a special case... 00342 00343 // Ensure that there is at least one char per line, otherwise, on 00344 // a very narrow document and huge chars, we could loop forever. 00345 // checkVerticalBreak takes care of moving down the lines where no 00346 // char should be, anyway. 00347 // Hmm, it doesn't really do so. To be continued... 00349 00350 // Or maybe we simply encountered a '\n' 00351 || lastChr->c == '\n' && parag->isNewLinesAllowed() && lastBreak > -1 ) 00352 { 00353 #ifdef DEBUG_FORMATTER 00354 kdDebug(32500) << "BREAKING" << endl; 00355 #endif 00356 //if ( wrapAtColumn() != -1 ) 00357 // minw = QMAX( minw, x + ww ); 00358 00359 bool hyphenated = false; 00360 // Hyphenation: check if we can break somewhere between lastBreak and i 00361 if ( settings->hyphenator() ) 00362 { 00363 int wordStart = QMAX(0, lastBreak+1); 00364 // Breaking after i isn't possible, i is too far already 00365 int maxlen = i - wordStart; // we can't accept to break after maxlen 00366 QString word = string->mid( wordStart, maxlen ); 00367 int wordEnd = i; 00368 // but we need to compose the entire word, to hyphenate it 00369 while ( wordEnd < len && !settings->isBreakable( string, wordEnd ) ) { 00370 word += string->at(wordEnd).c; 00371 wordEnd++; 00372 } 00373 if ( word.length() > 1 ) // don't call the hyphenator for empty or one-letter words 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 // If this word was already hyphenated (at the previous line), 00384 // don't break it there again. We can only break after firstChar. 00385 int minPos = QMAX( 0, (firstChar - &string->at(0)) - wordStart ); 00386 00387 // Check hyphenation positions from the end 00388 for ( int hypos = maxlen-1 ; hypos >= minPos ; --hypos ) 00389 if ( ( hyphens[hypos] % 2 ) // odd number -> can break there... 00390 && string->at(hypos + wordStart).format()->hyphenation() ) // ...if the user is ok with that 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 // No breakable char found -> break at current char (i.e. before 'i') 00412 if ( lastBreak < 0 ) { 00413 bool formatLine = true; 00414 if ( c->lineStart ) // ouch, empty line 00415 { 00416 // Remember where the biggest availableWidth was, so that if we really 00417 // find nowhere for this big character, we'll come back here. 00418 if ( availableWidth > maxAvailableWidth.second ) 00419 { 00420 maxAvailableWidth.first = y; 00421 maxAvailableWidth.second = availableWidth; 00422 } 00423 // Check if we're at the bottom of the doc, we won't find better then 00424 // (and the check further down would abort) 00425 // Instead we go back to where there was most width for it. 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 // In this case we want to actually format the line, 00435 // so we don't set emptyLine 00436 } 00437 else 00438 { 00439 // We don't know yet what to do with this line that needs to go down 00440 // Ideally KWTextFrameSet would tell us how much we need to move 00441 // ("validHeight" idea). For now we keep the old behavior: 00442 y += tmph; 00443 kdDebug(32500) << "KoTextFormatter: moving down empty line by h=" << tmph << ": y=" << y << endl; 00444 formatLine = false; // line is not ready yet 00445 } 00446 } 00447 if ( formatLine && i > 0 ) 00448 { 00449 // (combine lineStart->baseLine/lineStart->h and tmpBaseLine/tmph) 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, // ## correct? 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 //tminw = marg; // not in QRT? 00497 tmpWused = 0; 00498 } 00499 // recalc everything for 'i', it might still not be ok where it is... 00500 // (e.g. if there's no room at all on this line) 00501 // But we don't want to do this forever, so we check against maxY (if known) 00502 if ( formatLine && maxY > -1 ) 00503 { 00504 if ( parag->rect().y() + y < maxY ) 00505 { 00506 --i; // so that the ++i in for() is a noop 00507 continue; 00508 } 00509 else // we're after maxY, time to stop. 00510 { 00511 #ifdef DEBUG_FORMATTER 00512 kdDebug(32500) << "We're after maxY, time to stop." << endl; 00513 #endif 00514 // No solution for now. Hopefully KWord will create more pages... 00515 abort = true; 00516 } 00517 } 00518 // maxY not known -> keep going ('i' remains where it is) 00519 // (that's the initial QRT behaviour) 00520 } else { 00521 // If breaking means we're after maxY, then we won't do it. 00522 // Hopefully KWord will create more pages. 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 // Break the line at the last breakable character 00532 i = lastBreak; 00533 c = &string->at( i ); // The last char in the last line 00534 int spaceAfterLine = availableWidth - c->x; 00535 // ?? AFAICS we should always deduce the char's width from the available space.... 00536 //if ( string->isRightToLeft() && lastChr->c == '\n' ) 00537 spaceAfterLine -= c->width; 00538 00539 //else 00540 if ( c->c.unicode() == 0xad || hyphenated ) // soft hyphen or hyphenation 00541 { 00542 // Recalculate its width, the hyphen will appear finally (important for the parag rect) 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 ); // The first char in the new line 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 //tminw = marg; 00587 tmpWused = 0; 00588 c->lineStart = 1; // only do this if we will actually create a line for it 00589 continue; 00590 } 00591 } 00592 } else if ( lineStart && ( settings->isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) { 00593 // Breakable character 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 // (combine tmpBaseLine/tmph and this character) 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 //minw = QMAX( minw, tminw ); 00610 //tminw = marg + ww; 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 // (combine lineStart and tmpBaseLine/tmph) 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 // if h > initialHeight, call adjustMargins, and if the result is != initial[LR]Margin, 00628 // format this line again 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 //minw = x; 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 // ww and pixelww already calculated and stored, no need to duplicate 00667 // code like QRT does. 00668 ww = c->width; 00669 #ifndef REF_IS_LU 00670 pixelww = c->pixelwidth; 00671 #endif 00672 //tminw = x + ww; 00673 tmpWused = 0; 00674 } 00675 } 00676 00677 //kdDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl; 00678 if ( i < len - 2 || c->c != ' ' ) 00679 lastBreak = i; 00680 00681 } else { 00682 // Non-breakable character 00683 //tminw += ww; 00684 #ifdef DEBUG_FORMATTER_VERT 00685 kdDebug(32500) << " Non-breakable character: combining " << tmpBaseLine << "/" << tmph << " with " << c->ascent() << "/" << c->height() << endl; 00686 #endif 00687 // (combine tmpBaseLine/tmph and this character) 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 // pixelxadj is the adjustement to add to lu2pixel(x), to find pixelx 00704 // (pixelx would be too expensive to store directly since it would require an int) 00705 c->pixelxadj = pixelx - zh->layoutUnitToPixelX( x ); 00706 //c->pixelwidth = pixelww; // done as pixelx - lastPixelx below 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 // trailing space 00718 c->pixelwidth = zh->layoutUnitToPixelX( ww ); // was: pixelww; 00719 00720 lastPixelx = pixelx; 00721 #ifdef REF_IS_LU 00722 pixelx = zh->layoutUnitToPixelX( x ); // no accumulating rounding errors anymore 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 // ### hack. The last char in the paragraph is always invisible, and somehow sometimes has a wrong format. It changes between 00732 // layouting and printing. This corrects some layouting errors in BiDi mode due to this. 00733 if ( len > 1 /*&& !c->isAnchor()*/ ) { 00734 c->format()->removeRef(); 00735 c->setFormat( string->at( len - 2 ).format() ); 00736 c->format()->addRef(); 00737 } 00738 00739 // Finish formatting the last line 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 // (combine lineStart and tmpBaseLine/tmph) 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 // last line in a paragraph is not justified 00759 if ( align == Qt::AlignJustify ) 00760 align = Qt::AlignAuto; 00761 int space = availableWidth - x + c->width; // don't count the trailing space (it breaks e.g. centering) 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 //minw = QMAX( minw, tminw ); 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 // ##### Does OOo add margins or does it max them? 00776 //if ( parag->next() && doc && !doc->addMargins() ) 00777 // m = QMAX( m, parag->next()->topMargin() ); 00778 parag->setFullWidth( fullWidth ); 00779 //if ( parag->next() && parag->next()->isLineBreak() ) 00780 // m = 0; 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; // ### this can break with a variable right-margin 00787 //if ( !wrapEnabled || wrapAtColumn() != -1 ) 00788 // minw = QMAX(minw, wused); 00789 //thisminw = minw; 00790 00791 #ifdef DEBUG_FORMATTER 00792 // Sanity checking 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 // Helper for koFormatLine and koBidiReorderLine 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 ); // #### with nested tables this gets negative because of a bug I didn't find yet, so workaround for now. This also means non-left aligned nested tables do not work at the moment 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 // do alignment Auto == Left in this case 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 // End at "last-1", the last space ends up with a width of 0 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 ); // ### rounding problem, recalculate 00886 #endif 00887 } 00888 } 00889 } 00890 int current=0; 00891 int nc=0; // Not double, as we check it against 0 and to avoid gcc warnings 00892 KoTextFormat refFormat( *string->at(0).format() ); // we need a ref format, doesn't matter where it comes from 00893 for(int i=start;i<=last;++i) 00894 { 00895 KoTextFormat* format=string->at(i).format(); 00896 // End of underline 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 // Apply underline width "avg" from i-nc to i 00907 refFormat.setUnderLineWidth( avg ); 00908 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth ); 00909 nc=0; 00910 current=0; 00911 } 00912 // Inside underline 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(); //pointSize() is independent of {Sub,Super}Script in contrast to height() 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; //string->width( last ); 00926 // Add width of hyphen (so that it appears) 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 // collects one line of the paragraph and transforms it to visual order 00937 KoTextParagLineStart *KoTextFormatterCore::koBidiReorderLine( 00938 KoZoomHandler *zh, 00939 KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line, 00940 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space ) 00941 { 00942 // This comes from Qt (3.3.x) but seems wrong: the last space is where we draw 00943 // the "end of paragraph" sign, so it needs to be correctly positioned too. 00944 #if 0 00945 // ignore white space at the end of the line. 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 // fill string with logically ordered chars. 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 // now construct the reordered string out of the runs... 00977 00978 int numSpaces = 0; 00979 // set the correct alignment. This is a bit messy.... 00980 if( align == Qt::AlignAuto ) { 00981 // align according to directionality of the paragraph... 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 // TODO #ifndef REF_IS_LU or remove 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 // odd level, need to reverse the string 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 ); // ### rounding problem, recalculate 01026 } else if ( first ) { 01027 first = FALSE; 01028 if ( chr.c == ' ' ) // trailing space 01029 { 01030 //x -= chr.format()->width( ' ' ); 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 == ' ' ) // trailing space 01064 { 01065 //x -= chr.format()->width( ' ' ); 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 //kdDebug(32500) << "setting char " << pos << " at pos " << chr.x << endl; 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 //line->w = xmax /*+ 10*/; // Why +10 ? 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 }
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 06:54:18 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003