F:/KPlato/koffice/libs/kotext/KoTextFormatter.cpp

Aller à la documentation de ce fichier.
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., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KoTextFormatter.h"
00021 #include "KoTextParag.h"
00022 #include "KoTextFormat.h"
00023 #include "KoTextDocument.h"
00024 #include "KoTextZoomHandler.h"
00025 #include "kohyphen/kohyphen.h"
00026 #include "KoParagCounter.h"
00027 
00028 #include <kdebug.h>
00029 #include <assert.h>
00030 //Added by qt3to4:
00031 #include <Q3ValueList>
00032 #include <Q3PtrList>
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     KoTextZoomHandler *zh = doc->formattingZoomHandler();
00093     if ( c->c != '\t' || c->isCustom() ) {
00094         KoTextFormat *charFormat = c->format();
00095         if ( c->isCustom() ) {
00096             ww = c->customItem()->width;
00097             Q_ASSERT( ww >= 0 );
00098             ww = qMax(0, ww);
00099 #ifndef REF_IS_LU
00100             pixelww = zh->layoutUnitToPixelX( ww );
00101 #endif
00102         } else {
00103             ww = charFormat->charWidthLU( c, parag, i );
00104 #ifndef REF_IS_LU
00105             // 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, availableWidth );
00111         if ( nx < x )
00112             ww = availableWidth - x;
00113         else
00114             ww = nx - x;
00115 #ifdef DEBUG_FORMATTER
00116         kDebug(32500) << "nextTab for x=" << x << " returned nx=" << nx << "  (=> ww=" << ww << ")" << endl;
00117 #endif
00118 #ifndef REF_IS_LU
00119         pixelww = zh->layoutUnitToPixelX( ww );
00120 #endif
00121     }
00122     Q_ASSERT( ww >= 0 );
00123     c->width = ww;
00124     return qMakePair(ww, pixelww);
00125 }
00126 
00127 
00128 int KoTextFormatterCore::leftMargin( bool firstLine, bool includeFirstLineMargin /* = true */ ) const
00129 {
00130     int left = parag->leftMargin() + doc->leftMargin();
00131     if ( firstLine && !parag->string()->isRightToLeft() )
00132     {
00133         if ( includeFirstLineMargin )
00134             left += parag->firstLineMargin();
00135         // Add the width of the paragraph counter - first line of parag only.
00136         if( parag->counter() &&
00137             ( parag->counter()->alignment() == Qt::AlignLeft ||
00138               parag->counter()->alignment() == Qt::AlignLeft ) )
00139             left += parag->counterWidth(); // in LU pixels
00140     }
00141     return left;
00142 }
00143 
00144 int KoTextFormatterCore::rightMargin( bool firstLine ) const
00145 {
00146     int right = parag->rightMargin(); // 'rm' in QRT
00147     if ( firstLine && parag->string()->isRightToLeft() )
00148         right += parag->firstLineMargin();
00149     return right;
00150 }
00151 
00152 bool KoTextFormatterCore::format()
00153 {
00154     start = 0; // we don't do partial formatting yet
00155     KoTextString *string = parag->string();
00156     if ( start == 0 )
00157         c = &string->at( start );
00158     else
00159         c = 0;
00160 
00161     KoTextStringChar *firstChar = 0;
00162     int left = leftMargin( true, false );
00163     int initialLMargin = leftMargin( true );
00164 
00165     y = parag->breakableTopMargin();
00166     // #57555, top margin doesn't apply if parag at top of page
00167     // (but a portion of the margin can be needed, to complete the prev page)
00168     // So we apply formatVertically() on the top margin, to find where to break it.
00169     if ( !parag->prev() )
00170         y = 0; // no top margin on very first parag
00171     else if ( y )
00172     {
00173         int shift = doc->flow()->adjustFlow( parag->rect().y(),
00174                                              0 /*w, unused*/,
00175                                              y );
00176         if ( shift > 0 )
00177         {
00178             // The shift is in fact the amount of top-margin that should remain
00179             // The remaining portion should be eaten away.
00180             y = shift;
00181         }
00182 
00183     }
00184     // Now add the rest of the top margin (e.g. the one for the border)
00185     y += parag->topMargin() - parag->breakableTopMargin();
00186     int len = parag->length();
00187 
00188     int initialHeight = c->height(); // remember what adjustMargins was called with
00189 
00190     int currentRightMargin = rightMargin( true );
00191     int initialRMargin = currentRightMargin;
00192     // Those three things must be done before calling determineCharWidth
00193     i = start;
00194     parag->tabCache().clear();
00195     x = 0;
00196 
00197     // We need the width of the first char for adjustMargins
00198     // The result might not be 100% accurate when using a tab (it'll use x=0
00199     // but with counters/margins this might be different). This is why
00200     // we call determineCharWidth() again from within the loop.
00201     QPair<int, int> widths = determineCharWidth();
00202     int ww = widths.first; // width in layout units
00203 #ifndef REF_IS_LU
00204     int pixelww = widths.second; // width in pixels
00205 #endif
00206 
00207     // dw is the document width, i.e. the maximum available width, all included.
00208     // We are in a variable-width design, so it is returned by each call to adjustMargins.
00209     int dw = 0;
00210     doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight, // input params
00211                                 ww, initialLMargin, initialRMargin, dw,  // output params
00212                                 parag );
00213     //else dw = parag->documentVisibleWidth();
00214 
00215     x = initialLMargin; // as modified by adjustMargins
00216 
00217     int maxY = doc->flow()->availableHeight();
00218 
00219     availableWidth = dw - initialRMargin; // 'w' in QRT
00220 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH)
00221     kDebug(32500) << "KoTextFormatter::format formatting parag " << parag->paragId()
00222                    << " text:" << parag->string()->toString() << "\n"
00223                    << " left=" << left << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " availableWidth=" << availableWidth << " maxY=" << maxY << endl;
00224 #else
00225     if ( availableWidth == 0 )
00226         kDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, availableWidth=0" << endl;
00227     if ( maxY == 0 )
00228         kDebug(32500) << "KoTextFormatter::format " << parag->paragId() << " warning, maxY=0" << endl;
00229 #endif
00230     bool fullWidth = true;
00231     //int marg = left + initialRMargin;
00232 
00233     // minw is the really minimum width needed for this paragraph, i.e.
00234     // the width of the longest set of non-breakable characters together.
00235     // Currently unused.
00236     //int minw = 0;
00237 
00238     wused = 0;
00239 
00240     Q3ValueList<TemporaryWordData> tempWordData;
00241 
00242 #ifdef DEBUG_FORMATTER
00243     kDebug(32500) << "Initial KoTextParagLineStart at y=" << y << endl;
00244 #endif
00245     KoTextParagLineStart *lineStart = new KoTextParagLineStart( y, 0, 0 );
00246     parag->insertLineStart( 0, lineStart );
00247     int lastBreak = -1;
00248     // tmph, tmpBaseLine and tminw are used after the last breakable char
00249     // we don't know yet if we'll break there, or later.
00250     int tmpBaseLine = 0, tmph = 0;
00251     //int tminw = marg;
00252     int tmpWused = 0;
00253     bool lastWasNonInlineCustom = false;
00254     bool abort = false;
00255 
00256     int align = parag->alignment();
00257     if ( align == Qt::AlignLeft && doc->alignment() != Qt::AlignLeft )
00258         align = doc->alignment();
00259 
00260     int col = 0;
00261 
00262     maxAvailableWidth = qMakePair( 0, 0 );
00263 
00264     KoTextZoomHandler *zh = doc->formattingZoomHandler();
00265     int pixelx = zh->layoutUnitToPixelX( x );
00266     int lastPixelx = 0;
00267 
00268     KoTextStringChar* lastChr = 0;
00269     for ( ; i < len; ++i, ++col ) {
00270         if ( c )
00271             lastChr = c;
00272         c = &string->at( i );
00273         if ( i > 0 && (x > initialLMargin || ww == 0) || lastWasNonInlineCustom ) {
00274             c->lineStart = 0;
00275         } else {
00276             c->lineStart = 1;
00277             firstChar = c;
00278             tmph = c->height();
00279             tmpBaseLine = c->ascent();
00280 #ifdef DEBUG_FORMATTER_VERT
00281             kDebug(32500) << "New line, initializing tmpBaseLine=" << tmpBaseLine << " tmph=" << tmph << endl;
00282 #endif
00283         }
00284 
00285         if ( c->isCustom() && c->customItem()->placement() != KoTextCustomItem::PlaceInline )
00286             lastWasNonInlineCustom = true;
00287         else
00288             lastWasNonInlineCustom = false;
00289 
00290         QPair<int, int> widths = determineCharWidth();
00291         ww = widths.first;
00292         pixelww = widths.second;
00293 
00294         // We're "aborting" the formatting. This still means we need to set the
00295         // lineStart bools to false (trouble ahead, otherwise!), and while we're at
00296         // it we also calculate the widths etc.
00297         if ( abort ) {
00298             x += ww;
00299             c->x = x;
00300             continue; // yeah, this seems a bit confusing :)
00301         }
00302 
00303         //code from qt-3.1beta2
00304         if ( c->isCustom() && c->customItem()->ownLine() ) {
00305 #ifdef DEBUG_FORMATTER
00306             kDebug(32500) << "i=" << i << "/" << len << " custom item with ownline" << endl;
00307 #endif
00308             int rightMargin = currentRightMargin;
00309             x = left;
00310             doc->flow()->adjustMargins( y + parag->rect().y(), parag->rect().height(), 15,
00311                                         x, rightMargin, dw, parag );
00312             int w = dw - rightMargin;
00313             c->customItem()->resize( w - x );
00314             y += lineStart->h;
00315             lineStart = new KoTextParagLineStart( y, c->ascent(), c->height() );
00316             // Added for kotext (to be tested)
00317             lineStart->lineSpacing = parag->calculateLineSpacing( (int)parag->lineStartList().count()-1, i, i );
00318             lineStart->h += lineStart->lineSpacing;
00319             lineStart->w = dw;
00320             parag->insertLineStart( i, lineStart );
00321             tempWordData.clear();
00322             c->lineStart = 1;
00323             firstChar = c;
00324             x = 0xffffff;
00325             // Hmm, --i or setting lineStart on next char too?
00326             continue;
00327         }
00328 
00329 #ifdef DEBUG_FORMATTER
00330         kDebug(32500) << "c='" << QString(c->c) << "' i=" << i << "/" << len << " x=" << x << " ww=" << ww << " availableWidth=" << availableWidth << " (test is x+ww>aW) lastBreak=" << lastBreak << " isBreakable=" << settings->isBreakable(string, i) << endl;
00331 #endif
00332         // Wrapping at end of line - one big if :)
00333         if (
00334              // Check if should break (i.e. we are after the max X for the end of the line)
00335              ( /*wrapAtColumn() == -1 &&*/ x + ww > availableWidth &&
00336                ( lastBreak != -1 || settings->allowBreakInWords() ) )
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              // No point in breaking just for the trailing space (testcase: page numbers in TOC)
00344              && ( i < len-1 )
00345 
00346              // Ensure that there is at least one char per line, otherwise, on
00347              // a very narrow document and huge chars, we could loop forever.
00348              // checkVerticalBreak takes care of moving down the lines where no
00349              // char should be, anyway.
00350              // Hmm, it doesn't really do so. To be continued...
00352 
00353              // Or maybe we simply encountered a '\n'
00354              || lastChr && lastChr->c == '\n' && parag->isNewLinesAllowed() && lastBreak > -1 )
00355         {
00356 #ifdef DEBUG_FORMATTER
00357             kDebug(32500) << "BREAKING" << endl;
00358 #endif
00359             //if ( wrapAtColumn() != -1 )
00360             //    minw = qMax( minw, x + ww );
00361 
00362             bool hyphenated = false;
00363             // Hyphenation: check if we can break somewhere between lastBreak and i
00364             if ( settings->hyphenator() && !c->isCustom() )
00365             {
00366                 int wordStart = qMax(0, lastBreak+1);
00367                 // Breaking after i isn't possible, i is too far already
00368                 int maxlen = i - wordStart; // we can't accept to break after maxlen
00369                 QString word = string->mid( wordStart, maxlen );
00370                 int wordEnd = i;
00371                 // but we need to compose the entire word, to hyphenate it
00372                 while ( wordEnd < len && !settings->isBreakable( string, wordEnd ) ) {
00373                     word += string->at(wordEnd).c;
00374                     wordEnd++;
00375                 }
00376                 if ( word.length() > 1 ) // don't call the hyphenator for empty or one-letter words
00377                 {
00378                     QString lang = string->at(wordStart).format()->language();
00379                     char * hyphens = settings->hyphenator()->hyphens( word, lang );
00380 #if defined(DEBUG_HYPHENATION)
00381                     kDebug(32500) << "Hyphenation: word=" << word << " lang=" << lang << " hyphens=" << hyphens << " maxlen=" << maxlen << endl;
00382                     kDebug(32500) << "Parag indexes: wordStart=" << wordStart << " lastBreak=" << lastBreak << " i=" << i << endl;
00383 #endif
00384                     int hylen = strlen(hyphens);
00385                     Q_ASSERT( maxlen <= hylen );
00386                     // If this word was already hyphenated (at the previous line),
00387                     // don't break it there again. We can only break after firstChar.
00388                     int minPos = qMax( 0, int(firstChar - &string->at(0)) - wordStart );
00389 
00390                     // Check hyphenation positions from the end
00391                     for ( int hypos = maxlen-1 ; hypos >= minPos ; --hypos )
00392                         if ( ( hyphens[hypos] % 2 ) // odd number -> can break there...
00393                                && string->at(hypos + wordStart).format()->hyphenation() ) // ...if the user is ok with that
00394                         {
00395                             lineStart->hyphenated = true;
00396                             lastBreak = hypos + wordStart;
00397                             hyphenated = true;
00398 #if defined(DEBUG_FORMATTER) || defined(DEBUG_FORMATTER_WIDTH) || defined(DEBUG_HYPHENATION)
00399                             kDebug(32500) << "Hyphenation: will break at " << lastBreak << " using tempworddata at position " << hypos << "/" << tempWordData.size() << endl;
00400 #endif
00401                             if ( hypos < (int)tempWordData.size() )
00402                             {
00403                                 const TemporaryWordData& twd = tempWordData[ hypos ];
00404                                 lineStart->baseLine = twd.baseLine;
00405                                 lineStart->h = twd.height;
00406                                 tmpWused = twd.lineWidth;
00407                             }
00408                             break;
00409                         }
00410                     delete[] hyphens;
00411                 }
00412             }
00413 
00414             // No breakable char found -> break at current char (i.e. before 'i')
00415             if ( lastBreak < 0 ) {
00416                 // Remember if this is the start of a line; testing c->lineStart after breaking
00417                 // is always true...
00418                 const bool emptyLine = c->lineStart;
00419                 if ( emptyLine ) // ouch, empty line
00420                 {
00421                     // This happens when there is a very wide character (e.g. inline table),
00422                     // or a very narrow passage between frames. In the second case we'll
00423                     // have more room below, in the first case we might not.
00424                     // So we look below, and come back if we don't find better.
00425 
00426                     // Remember where the biggest availableWidth was, so that if we really
00427                     // find nowhere for this big character, we'll come back here.
00428                     if ( availableWidth > maxAvailableWidth.second )
00429                     {
00430                         maxAvailableWidth.first = y;
00431                         maxAvailableWidth.second = availableWidth;
00432                     }
00433                     // Check if we're at the bottom of the doc, we won't find better then
00434                     // (and the check further down would abort)
00435                     if ( ( maxY > -1 && parag->rect().y() + y >= maxY ) || tmph <= 0 )
00436                     {
00437                         // Here we have to distinguish "table wider than document" (#112269)
00438                         // and "not enough room for chars next to a big frame".
00439                         // In the first case a new page wouldn't help, in the second it would.
00440                         // ### Real fix: asking for the max width on a not-yet-created next page.
00441                         // For now I just approximate that with the flow width.
00442                         if ( c->width >= doc->flow()->width() )
00443                         {
00444                             // OK we go back to where there was most width for it.
00445                             kDebug(32500) << parag->rect().y() + y << " over maxY=" << maxY
00446                                            << " -> final choice for the line: y=" << maxAvailableWidth.first << endl;
00447                             y = maxAvailableWidth.first;
00448                             if ( availableWidth )
00449                                 Q_ASSERT( maxAvailableWidth.second != 0 );
00450                             lineStart->y = y;
00451                             maxAvailableWidth = qMakePair( 0, 0 ); // clear it
00452                         }
00453                         else
00454                         {
00455                             // "small" chars and not enough width here, abort and hope for a new page.
00456 #ifdef DEBUG_FORMATTER
00457                             if ( tmph <= 0 )
00458                                 kDebug(32500) << "Line has a height of " << tmph << ", let's stop." << endl;
00459                             else
00460                                 kDebug(32500) << "We're after maxY, time to stop." << endl;
00461 #endif
00462                             // No solution for now. Hopefully KWord will create more pages...
00463                             abort = true;
00464                         }
00465                     }
00466                     else
00467                     {
00468                         // We don't know yet what to do with this line that needs to go down
00469                         // Ideally KWTextFrameSet would tell us how much we need to move
00470                         // ("validHeight" idea). For now we keep the old behavior:
00471                         y += tmph;
00472                         kDebug(32500) << "KoTextFormatter: moving down empty line by h=" << tmph << ": y=" << y << endl;
00473 
00474                         --i; // so that the ++i in for() is a noop
00475                         continue;
00476                     }
00477                 }
00478                 if ( !emptyLine && i > 0 )
00479                 {
00480                     // (combine lineStart->baseLine/lineStart->h and tmpBaseLine/tmph)
00481                     int belowBaseLine = qMax( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00482                     lineStart->baseLine = qMax( (int)lineStart->baseLine, tmpBaseLine );
00483                     lineStart->h = lineStart->baseLine + belowBaseLine;
00484                     lineStart->w = dw;
00485 
00486                     KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c-1, align, availableWidth - x );
00487                     y += lineStart->h;
00488                     lineStart = lineStart2;
00489 #ifdef DEBUG_FORMATTER
00490                     int linenr = parag->lineStartList().count()-1;
00491                     kDebug(32500) << "line " << linenr << " done (breaking at current char). y now " << y << endl;
00492 #endif
00493                     tmph = c->height();
00494 
00495                     initialRMargin = currentRightMargin;
00496                     x = left;
00497                     doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00498                                                 ww, // ## correct?
00499                                                 x, initialRMargin, dw, parag );
00500 
00501                     pixelx = zh->layoutUnitToPixelX( x );
00502                     initialHeight = tmph;
00503                     initialLMargin = x;
00504                     availableWidth = dw - initialRMargin;
00505                     if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
00506                         int nx = parag->nextTab( i, x, availableWidth );
00507                         if ( nx < x )
00508                             ww = availableWidth - x;
00509                         else
00510                             ww = nx - x;
00511                     }
00512                     if ( x != left || availableWidth != dw )
00513                         fullWidth = false;
00514                     lineStart->y = y;
00515                     parag->insertLineStart( i, lineStart );
00516                     tempWordData.clear();
00517                     lineStart->baseLine = c->ascent();
00518                     lineStart->h = c->height();
00519                     c->lineStart = 1;
00520                     firstChar = c;
00521                     tmpBaseLine = lineStart->baseLine;
00522                     lastBreak = -1;
00523                     col = 0;
00524                     //tminw = marg; // not in QRT?
00525                     tmpWused = 0;
00526                 }
00527                 // recalc everything for 'i', it might still not be ok where it is...
00528                 // (e.g. if there's no room at all on this line)
00529                 // But we don't want to do this forever, so we check against maxY (if known)
00530                 // [except if we come here after "final choice for empty line"!]
00531                 if ( !emptyLine && maxY > -1 )
00532                 {
00533                     if ( parag->rect().y() + y < maxY )
00534                     {
00535 #ifdef DEBUG_FORMATTER
00536                         kDebug(32500) << "Re-checking formatting for character " << i << endl;
00537 #endif
00538                         --i; // so that the ++i in for() is a noop
00539                         continue;
00540                     }
00541                     else // we're after maxY, time to stop.
00542                     {
00543 #ifdef DEBUG_FORMATTER
00544                         kDebug(32500) << "We're after maxY, time to stop." << endl;
00545 #endif
00546                         // No solution for now. Hopefully KWord will create more pages...
00547                         abort = true;
00548                     }
00549                 }
00550                 // maxY not known (or "final choice for empty line") -> keep going ('i' remains where it is)
00551                 // (in case of maxY not known, this is the initial QRT behaviour)
00552             } else {
00553                 // If breaking means we're after maxY, then we won't do it.
00554                 // Hopefully KWord will create more pages.
00555                 if ( maxY > -1 && parag->rect().y() + y + lineStart->h >= maxY ) {
00556 #ifdef DEBUG_FORMATTER
00557                     kDebug(32500) << "We're after maxY, time to stop." << endl;
00558 #endif
00559                     abort = true;
00560                 }
00561                 else
00562                 {
00563                     // Break the line at the last breakable character
00564                     i = lastBreak;
00565                     c = &string->at( i ); // The last char in the last line
00566                     int spaceAfterLine = availableWidth - c->x;
00567                     // ?? AFAICS we should always deduce the char's width from the available space....
00568                     //if ( string->isRightToLeft() && lastChr->c == '\n' )
00569                     spaceAfterLine -= c->width;
00570 
00571                     //else
00572                     if ( c->c.unicode() == 0xad || hyphenated ) // soft hyphen or hyphenation
00573                     {
00574                         // Recalculate its width, the hyphen will appear finally (important for the parag rect)
00575                         int width = KoTextZoomHandler::ptToLayoutUnitPt( c->format()->refFontMetrics().width( QChar(0xad) ) );
00576                         if ( c->c.unicode() == 0xad )
00577                             c->width = width;
00578                         spaceAfterLine -= width;
00579                     }
00580                     KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, spaceAfterLine );
00581                     lineStart->w = dw;
00582                     y += lineStart->h;
00583                     lineStart = lineStart2;
00584 #ifdef DEBUG_FORMATTER
00585                     kDebug(32500) << "Breaking at a breakable char (" << i << "). linenr=" << parag->lineStartList().count()-1 << " y=" << y << endl;
00586 #endif
00587 
00588                     c = &string->at( i + 1 ); // The first char in the new line
00589 #ifdef DEBUG_FORMATTER
00590                     kDebug(32500) << "Next line will start at i+1=" << i+1 << ", char=" << QString(c->c) << endl;
00591 #endif
00592                     tmph = c->height();
00593 
00594                     initialRMargin = currentRightMargin;
00595                     x = left;
00596                     doc->flow()->adjustMargins( y + parag->rect().y(), tmph,
00597                                                 c->width,
00598                                                 x, initialRMargin, dw, parag );
00599 
00600                     pixelx = zh->layoutUnitToPixelX( x );
00601                     initialHeight = tmph;
00602                     initialLMargin = x;
00603                     availableWidth = dw - initialRMargin;
00604                     if ( x != left || availableWidth != dw )
00605                         fullWidth = false;
00606                     lineStart->y = y;
00607                     parag->insertLineStart( i + 1, lineStart );
00608                     tempWordData.clear();
00609                     lineStart->baseLine = c->ascent();
00610                     lineStart->h = c->height();
00611                     firstChar = c;
00612                     tmpBaseLine = lineStart->baseLine;
00613                     lastBreak = -1;
00614                     col = 0;
00615                     //tminw = marg;
00616                     tmpWused = 0;
00617                     c->lineStart = 1; // only do this if we will actually create a line for it
00618                     continue;
00619                 }
00620             }
00621         } else if ( lineStart && ( settings->isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) {
00622             // Breakable character
00623             if ( len <= 2 || i < len - 1 ) {
00624 #ifdef DEBUG_FORMATTER_VERT
00625                 kDebug(32500) << " Breakable character (i=" << i << " len=" << len << "):"
00626                                << " combining " << tmpBaseLine << "/" << tmph
00627                                << " with " << c->ascent() << "/" << c->height() << endl;
00628 #endif
00629                 // (combine tmpBaseLine/tmph and this character)
00630                 int belowBaseLine = qMax( tmph - tmpBaseLine, c->height() - c->ascent() );
00631                 tmpBaseLine = qMax( tmpBaseLine, c->ascent() );
00632                 tmph = tmpBaseLine + belowBaseLine;
00633 #ifdef DEBUG_FORMATTER_VERT
00634                 kDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00635 #endif
00636             }
00637             tempWordData.clear();
00638             //minw = qMax( minw, tminw );
00639             //tminw = marg + ww;
00640             wused = qMax( wused, tmpWused );
00641 #ifdef DEBUG_FORMATTER_WIDTH
00642             kDebug(32500) << " Breakable character (i=" << i << " len=" << len << "): wused=" << wused << endl;
00643 #endif
00644             tmpWused = 0;
00645             // (combine lineStart and tmpBaseLine/tmph)
00646 #ifdef DEBUG_FORMATTER_VERT
00647             kDebug(32500) << "Breakable character: combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00648 #endif
00649             int belowBaseLine = qMax( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00650             lineStart->baseLine = qMax( (int)lineStart->baseLine, tmpBaseLine );
00651             lineStart->h = lineStart->baseLine + belowBaseLine;
00652             lineStart->w = dw;
00653 #ifdef DEBUG_FORMATTER_VERT
00654             kDebug(32500) << " -> line baseLine/height : " << lineStart->baseLine << "/" << lineStart->h << endl;
00655 #endif
00656             // if h > initialHeight,  call adjustMargins, and if the result is != initial[LR]Margin,
00657             // format this line again
00658             if ( lineStart->h > initialHeight )
00659             {
00660                 bool firstLine = ( firstChar == &string->at( 0 ) );
00661                 int newLMargin = leftMargin( firstLine );
00662                 int newRMargin = rightMargin( firstLine );
00663                 int newPageWidth = dw;
00664                 initialHeight = lineStart->h;
00665                 doc->flow()->adjustMargins( y + parag->rect().y(), initialHeight,
00666                                             firstChar->width,
00667                                             newLMargin, newRMargin, newPageWidth, parag );
00668 
00669 #ifdef DEBUG_FORMATTER
00670                 kDebug(32500) << "new height: " << initialHeight << " => left=" << left << " first-char=" << (firstChar==&string->at(0)) << " newLMargin=" << newLMargin << " newRMargin=" << newRMargin << endl;
00671 #endif
00672                 if ( newLMargin != initialLMargin || newRMargin != initialRMargin || newPageWidth != dw )
00673                 {
00674 #ifdef DEBUG_FORMATTER
00675                     kDebug(32500) << "formatting again" << endl;
00676 #endif
00677                     i = (firstChar - &string->at(0));
00678                     x = newLMargin;
00679                     pixelx = zh->layoutUnitToPixelX( x );
00680                     availableWidth = dw - newRMargin;
00681                     initialLMargin = newLMargin;
00682                     initialRMargin = newRMargin;
00683                     dw = newPageWidth;
00684                     c = &string->at( i );
00685                     tmph = c->height();
00686                     tmpBaseLine = c->ascent();
00687                     lineStart->h = tmph;
00688                     lineStart->baseLine = tmpBaseLine;
00689                     lastBreak = -1;
00690                     col = 0;
00691                     //minw = x;
00692 #ifdef DEBUG_FORMATTER
00693                     kDebug(32500) << "Restarting with i=" << i << " x=" << x << " y=" << y << " tmph=" << tmph << " initialHeight=" << initialHeight << " initialLMargin=" << initialLMargin << " initialRMargin=" << initialRMargin << " y=" << y << endl;
00694 #endif
00695                     // ww and pixelww already calculated and stored, no need to duplicate
00696                     // code like QRT does.
00697                     ww = c->width;
00698 #ifndef REF_IS_LU
00699                     pixelww = c->pixelwidth;
00700 #endif
00701                     //tminw = x + ww;
00702                     tmpWused = 0;
00703                 }
00704             }
00705 
00706             //kDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl;
00707             if ( i < len - 2 || c->c != ' ' )
00708                 lastBreak = i;
00709 
00710         } else if ( i < len - 1 ) { // ignore height of trailing space
00711             // Non-breakable character
00712             //tminw += ww;
00713 #ifdef DEBUG_FORMATTER_VERT
00714             kDebug(32500) << " Non-breakable character: combining " << tmpBaseLine << "/" << tmph << " with " << c->ascent() << "/" << c->height() << endl;
00715 #endif
00716             // (combine tmpBaseLine/tmph and this character)
00717             int belowBaseLine = qMax( tmph - tmpBaseLine, c->height() - c->ascent() );
00718             tmpBaseLine = qMax( tmpBaseLine, c->ascent() );
00719             tmph = tmpBaseLine + belowBaseLine;
00720 #ifdef DEBUG_FORMATTER_VERT
00721             kDebug(32500) << " -> tmpBaseLine/tmph : " << tmpBaseLine << "/" << tmph << endl;
00722 #endif
00723 
00724             TemporaryWordData twd;
00725             twd.baseLine = tmpBaseLine;
00726             twd.height = tmph;
00727             twd.lineWidth = tmpWused;
00728             tempWordData.append( twd );
00729         }
00730 
00731         c->x = x;
00732         // pixelxadj is the adjustement to add to lu2pixel(x), to find pixelx
00733         // (pixelx would be too expensive to store directly since it would require an int)
00734         c->pixelxadj = pixelx - zh->layoutUnitToPixelX( x );
00735         //c->pixelwidth = pixelww; // done as pixelx - lastPixelx below
00736 #ifdef DEBUG_FORMATTER
00737         kDebug(32500) << "LU: x=" << x << " [equiv. to pix=" << zh->layoutUnitToPixelX( x ) << "] ; PIX: x=" << pixelx << "  --> adj=" << c->pixelxadj << endl;
00738 #endif
00739 
00740         x += ww;
00741 
00742         if ( i > 0 && lastChr )
00743             lastChr->pixelwidth = pixelx - lastPixelx;
00744         if ( i < len - 1 )
00745             tmpWused = qMax( tmpWused, x );
00746         else // trailing space
00747             c->pixelwidth = zh->layoutUnitToPixelX( ww ); // was: pixelww;
00748 
00749         lastPixelx = pixelx;
00750 #ifdef REF_IS_LU
00751         pixelx = zh->layoutUnitToPixelX( x ); // no accumulating rounding errors anymore
00752 #else
00753         pixelx += pixelww;
00754 #endif
00755 #ifdef DEBUG_FORMATTER
00756         kDebug(32500) << "LU: added " << ww << " -> now x=" << x << " ; PIX: added " << pixelww << " -> now pixelx=" << pixelx << endl;
00757 #endif
00758     }
00759 
00760     // ### hack. The last char in the paragraph is always invisible, and somehow sometimes has a wrong format. It changes between
00761     // layouting and printing. This corrects some layouting errors in BiDi mode due to this.
00762     if ( len > 1 /*&& !c->isAnchor()*/ ) {
00763         c->format()->removeRef();
00764         c->setFormat( string->at( len - 2 ).format() );
00765         c->format()->addRef();
00766     }
00767 
00768     // Finish formatting the last line
00769     if ( lineStart ) {
00770 #ifdef DEBUG_FORMATTER
00771         kDebug(32500) << "Last Line.... linenr=" << (int)parag->lineStartList().count()-1 << endl;
00772 #endif
00773 #ifdef DEBUG_FORMATTER_VERT
00774         kDebug(32500) << "Last Line... Combining " << lineStart->baseLine << "/" << lineStart->h << " with " << tmpBaseLine << "/" << tmph << endl;
00775 #endif
00776         // (combine lineStart and tmpBaseLine/tmph)
00777         int belowBaseLine = qMax( lineStart->h - lineStart->baseLine, tmph - tmpBaseLine );
00778         lineStart->baseLine = qMax( (int)lineStart->baseLine, tmpBaseLine );
00779         lineStart->h = lineStart->baseLine + belowBaseLine;
00780         lineStart->w = dw;
00781 #ifdef DEBUG_FORMATTER_WIDTH
00782         kDebug(32500) << "Last line: w = dw = " << dw << endl;
00783 #endif
00784 #ifdef DEBUG_FORMATTER_VERT
00785         kDebug(32500) << " -> lineStart->baseLine/lineStart->h : " << lineStart->baseLine << "/" << lineStart->h << endl;
00786 #endif
00787         // last line in a paragraph is not justified
00788         if ( align == Qt::AlignJustify )
00789             align = Qt::AlignLeft;
00790         int space = availableWidth - x + c->width; // don't count the trailing space (it breaks e.g. centering)
00791         KoTextParagLineStart *lineStart2 = koFormatLine( zh, parag, string, lineStart, firstChar, c, align, space );
00792         delete lineStart2;
00793     }
00794 
00795     //minw = qMax( minw, tminw );
00796     wused = qMax( wused, tmpWused );
00797 #ifdef DEBUG_FORMATTER_WIDTH
00798     kDebug(32500) << "Done, wused=" << wused << endl;
00799 #endif
00800 
00801     int m = parag->bottomMargin();
00802     // ##### Does OOo add margins or does it max them?
00803     //if ( parag->next() && !doc->addMargins() )
00804     //  m = qMax( m, parag->next()->topMargin() );
00805     parag->setFullWidth( fullWidth );
00806     //if ( parag->next() && parag->next()->isLineBreak() )
00807     //    m = 0;
00808 #ifdef DEBUG_FORMATTER_VERT
00809     kDebug(32500) << "Adding height of last line(" << lineStart->h << ") and bottomMargin(" << m << ") to y(" << y << ") => " << y+lineStart->h+m << endl;
00810 #endif
00811     y += lineStart->h + m;
00812 
00813     tmpWused += currentRightMargin; // ### this can break with a variable right-margin
00814     //if ( wrapAtColumn() != -1  )
00815     //    minw = qMax(minw, wused);
00816     //thisminw = minw;
00817 
00818 #ifdef DEBUG_FORMATTER
00819     // Sanity checking
00820     int numberOfLines = 0;
00821     QString charPosList;
00822     for ( int i = 0 ; i < len; ++i ) {
00823         KoTextStringChar *chr = &string->at( i );
00824         if ( i == 0 )
00825             assert( chr->lineStart );
00826         if ( chr->lineStart ) {
00827             ++numberOfLines;
00828             charPosList += QString::number(i) + ' ';
00829         }
00830     }
00831     kDebug(32500) << parag->lineStartList().count() << " lines. " << numberOfLines << " chars with lineStart set: " << charPosList << endl;
00832     assert( numberOfLines == (int)parag->lineStartList().count() );
00833 #endif
00834     return !abort;
00835 }
00836 
00837 // Helper for koFormatLine and koBidiReorderLine
00838 void KoTextFormatterCore::moveChar( KoTextStringChar& chr, KoTextZoomHandler *zh,
00839                                     int deltaX, int deltaPixelX )
00840 {
00841 #ifndef REF_IS_LU
00842     int pixelx = chr.pixelxadj + zh->layoutUnitToPixelX( chr.x );
00843 #endif
00844     chr.x += deltaX;
00845 #ifndef REF_IS_LU
00846     chr.pixelxadj = pixelx + deltaPixelX - zh->layoutUnitToPixelX( chr.x );
00847 #endif
00848 }
00849 
00850 KoTextParagLineStart *KoTextFormatterCore::koFormatLine(
00851     KoTextZoomHandler *zh,
00852     KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
00853     KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00854 {
00855     KoTextParagLineStart* ret = 0;
00856 #if 0 // TODO RTL SUPPORT
00857     if( string->isBidi() ) {
00858         ret = koBidiReorderLine( zh, parag, string, line, startChar, lastChar, align, space );
00859     } else
00860 #endif
00861     {
00862         int start = (startChar - &string->at(0));
00863         int last = (lastChar - &string->at(0) );
00864 
00865         if (space < 0)
00866             space = 0;
00867 
00868         // do alignment Auto == Left in this case
00869         if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
00870             if ( align & Qt::AlignHCenter )
00871                 space /= 2;
00872             int toAddPix = zh->layoutUnitToPixelX( space );
00873             for ( int j = last; j >= start; --j ) {
00874                 KoTextStringChar &chr = string->at( j );
00875                 moveChar( chr, zh, space, toAddPix );
00876             }
00877         } else if ( align & Qt::AlignJustify ) {
00878             int numSpaces = 0;
00879             // End at "last-1", the last space ends up with a width of 0
00880             for ( int j = last-1; j >= start; --j ) {
00882                 if ( string->at( j ).c == '\t' ) {
00883                     start = j+1;
00884                     break;
00885                 }
00886                 if( settings->isStretchable( string, j ) ) {
00887                     numSpaces++;
00888                 }
00889             }
00890             int toAdd = 0;
00891             int toAddPix = 0;
00892             for ( int k = start + 1; k <= last; ++k ) {
00893                 KoTextStringChar &chr = string->at( k );
00894                 if ( toAdd != 0 )
00895                     moveChar( chr, zh, toAdd, toAddPix );
00896                 if( settings->isStretchable( string, k ) && numSpaces ) {
00897                     int s = space / numSpaces;
00898                     toAdd += s;
00899                     toAddPix = zh->layoutUnitToPixelX( toAdd );
00900                     space -= s;
00901                     numSpaces--;
00902                     chr.width += s;
00903 #ifndef REF_IS_LU
00904                     chr.pixelwidth += zh->layoutUnitToPixelX( s ); // ### rounding problem, recalculate
00905 #endif
00906                 }
00907             }
00908         }
00909         int current=0;
00910         int nc=0; // Not double, as we check it against 0 and to avoid gcc warnings
00911         KoTextFormat refFormat( *string->at(0).format() ); // we need a ref format, doesn't matter where it comes from
00912         for(int i=start;i<=last;++i)
00913         {
00914             KoTextFormat* format=string->at(i).format();
00915             // End of underline
00916             if ( (((!format->underline())&&
00917                    (!format->doubleUnderline())&&
00918                    (!format->waveUnderline())&&
00919                    (format->underlineType()!=KoTextFormat::U_SIMPLE_BOLD))
00920                   || i == last)
00921                  && nc )
00922             {
00923                 double avg=static_cast<double>(current)/nc;
00924                 avg/=18.0;
00925                 // Apply underline width "avg" from i-nc to i
00926                 refFormat.setUnderLineWidth( avg );
00927                 parag->setFormat( i-nc, i, &refFormat, true, KoTextFormat::UnderLineWidth );
00928                 nc=0;
00929                 current=0;
00930             }
00931             // Inside underline
00932             else if(format->underline()||
00933                     format->waveUnderline()||
00934                     format->doubleUnderline()||
00935                     (format->underlineType() == KoTextFormat::U_SIMPLE_BOLD))
00936             {
00937                 ++nc;
00938                 current += format->pointSize(); //pointSize() is independent of {Sub,Super}Script in contrast to height()
00939             }
00940         }
00941 #if 0
00942         if ( last >= 0 && last < string->length() ) {
00943             KoTextStringChar &chr = string->at( last );
00944             line->w = chr.x + chr.width; //string->width( last );
00945             // Add width of hyphen (so that it appears)
00946             if ( line->hyphenated )
00947                 line->w += KoTextZoomHandler::ptToLayoutUnitPt( chr.format()->refFontMetrics().width( QChar(0xad) ) );
00948         } else
00949             line->w = 0;
00950 #endif
00951 
00952         ret = new KoTextParagLineStart();
00953     }
00954 
00955     // Now calculate and add linespacing
00956     const int start = (startChar - &string->at(0));
00957     const int last = (lastChar - &string->at(0) );
00958     line->lineSpacing = parag->calculateLineSpacing( (int)parag->lineStartList().count()-1, start, last );
00959     line->h += line->lineSpacing;
00960 
00961     return ret;
00962 }
00963 
00964 // TODO RTL SUPPORT
00965 #if 0
00966 // collects one line of the paragraph and transforms it to visual order
00967 KoTextParagLineStart *KoTextFormatterCore::koBidiReorderLine(
00968     KoTextZoomHandler *zh,
00969     KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
00970     KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
00971 {
00972     // This comes from Qt (3.3.x) but seems wrong: the last space is where we draw
00973     // the "end of paragraph" sign, so it needs to be correctly positioned too.
00974 #if 0
00975     // ignore white space at the end of the line.
00976     int endSpaces = 0;
00977     while ( lastChar > startChar && lastChar->whiteSpace ) {
00978         space += lastChar->format()->width( ' ' );
00979         --lastChar;
00980         ++endSpaces;
00981     }
00982 #endif
00983 
00984     int start = (startChar - &text->at(0));
00985     int last = (lastChar - &text->at(0) );
00986 #ifdef DEBUG_FORMATTER
00987     kDebug(32500) << "*KoTextFormatter::koBidiReorderLine from " << start << " to " << last << " space=" << space << " startChar->x=" << startChar->x << endl;
00988 #endif
00989     KoBidiControl *control = new KoBidiControl( line->context(), line->status );
00990     QString str;
00991     str.setUnicode( 0, last - start + 1 );
00992     // fill string with logically ordered chars.
00993     KoTextStringChar *ch = startChar;
00994     QChar *qch = (QChar *)str.unicode();
00995     while ( ch <= lastChar ) {
00996         *qch = ch->c;
00997         qch++;
00998         ch++;
00999     }
01000     int x = startChar->x;
01001 
01002     Q3PtrList<KoTextRun> *runs;
01003     runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01004                                          (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01005 
01006     // now construct the reordered string out of the runs...
01007 
01008     int numSpaces = 0;
01009     // set the correct alignment. This is a bit messy....
01010     if( align == Qt::AlignLeft ) {
01011         // align according to directionality of the paragraph...
01012         if ( text->isRightToLeft() )
01013             align = Qt::AlignRight;
01014     }
01015 
01016     if ( align & Qt::AlignHCenter ) {
01017         x += space/2;
01018     } else if ( align & Qt::AlignRight ) {
01019         x += space;
01020     } else if ( align & Qt::AlignJustify ) {
01021         for ( int j = last - 1; j >= start; --j ) {
01023             if ( text->at( j ).c == '\t' ) {
01024                 start = j+1;
01025                 break;
01026             }
01027             if( settings->isStretchable( text, j ) ) {
01028                 numSpaces++;
01029             }
01030         }
01031     }
01032 // TODO #ifndef REF_IS_LU or remove
01033     int pixelx = zh->layoutUnitToPixelX( x );
01034     int toAdd = 0;
01035     int toAddPix = 0;
01036     bool first = true;
01037     KoTextRun *r = runs->first();
01038     int xmax = -0xffffff;
01039     while ( r ) {
01040 #ifdef DEBUG_FORMATTER
01041         kDebug(32500) << "koBidiReorderLine level: " << r->level << endl;
01042 #endif
01043         if(r->level %2) {
01044             // odd level, need to reverse the string
01045             int pos = r->stop + start;
01046             while(pos >= r->start + start) {
01047                 KoTextStringChar &chr = text->at(pos);
01048                 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01049                     int s = space / numSpaces;
01050                     toAdd += s;
01051                     toAddPix = zh->layoutUnitToPixelX( toAdd );
01052                     space -= s;
01053                     numSpaces--;
01054                     chr.width += s;
01055                     chr.pixelwidth += zh->layoutUnitToPixelX( s ); // ### rounding problem, recalculate
01056                 } else if ( first ) {
01057                     first = false;
01058                     if ( chr.c == ' ' ) // trailing space
01059                     {
01060                         //x -= chr.format()->width( ' ' );
01061                         x -= chr.width;
01062                         pixelx -= chr.pixelwidth;
01063                     }
01064                 }
01065                 chr.x = x + toAdd;
01066                 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01067 #ifdef DEBUG_FORMATTER
01068                 kDebug(32500) << "koBidiReorderLine: pos=" << pos << " x(LU)=" << x << " toAdd(LU)=" << toAdd << " -> chr.x=" << chr.x << " pixelx=" << pixelx << "+" << zh->layoutUnitToPixelX( toAdd ) << ", pixelxadj=" << pixelx+zh->layoutUnitToPixelX( toAdd )-zh->layoutUnitToPixelX( chr.x ) << endl;
01069 #endif
01070                 chr.rightToLeft = true;
01071                 chr.startOfRun = false;
01072                 int ww = chr.width;
01073                 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01074                 x += ww;
01075                 pixelx += chr.pixelwidth;
01076 #ifdef DEBUG_FORMATTER
01077                 kDebug(32500) << "              ww=" << ww << " adding to x, now " << x << ". pixelwidth=" << chr.pixelwidth << " adding to pixelx, now " << pixelx << " xmax=" << xmax << endl;
01078 #endif
01079                 pos--;
01080             }
01081         } else {
01082             int pos = r->start + start;
01083             while(pos <= r->stop + start) {
01084                 KoTextStringChar& chr = text->at(pos);
01085                 if( numSpaces && !first && settings->isBreakable( text, pos ) ) {
01086                     int s = space / numSpaces;
01087                     toAdd += s;
01088                     toAddPix = zh->layoutUnitToPixelX( toAdd );
01089                     space -= s;
01090                     numSpaces--;
01091                 } else if ( first ) {
01092                     first = false;
01093                     if ( chr.c == ' ' ) // trailing space
01094                     {
01095                         //x -= chr.format()->width( ' ' );
01096                         x -= chr.width;
01097                         pixelx -= chr.pixelwidth;
01098                     }
01099                 }
01100                 chr.x = x + toAdd;
01101                 chr.pixelxadj = pixelx + toAddPix - zh->layoutUnitToPixelX( chr.x );
01102                 chr.rightToLeft = false;
01103                 chr.startOfRun = false;
01104                 int ww = chr.width;
01105                 //kDebug(32500) << "setting char " << pos << " at pos " << chr.x << endl;
01106                 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01107                 x += ww;
01108                 pixelx += chr.pixelwidth;
01109                 pos++;
01110             }
01111         }
01112         text->at( r->start + start ).startOfRun = true;
01113         r = runs->next();
01114     }
01115 
01116     //line->w = xmax /*+ 10*/; // Why +10 ?
01117     KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01118     delete control;
01119     delete runs;
01120     return ls;
01121 }
01122 #endif
01123 
01124 void KoTextFormatter::postFormat( KoTextParag* parag )
01125 {
01126     parag->fixParagWidth( viewFormattingChars() );
01127 }

Généré le Wed Nov 22 23:41:09 2006 pour KPlato par  doxygen 1.5.1-p1