xref: /AOO41X/main/sw/source/core/text/itrform2.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 #include "hintids.hxx"
28 
29 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
30 #include <com/sun/star/i18n/ScriptType.hdl>
31 #endif
32 #include <editeng/lspcitem.hxx>
33 #include <txtftn.hxx>
34 #include <fmtftn.hxx>
35 #include <ftninfo.hxx>
36 #include <charfmt.hxx>
37 #include <editeng/charrotateitem.hxx>
38 #include <layfrm.hxx>       // GetFrmRstHeight, etc
39 #include <viewsh.hxx>
40 #include <viewopt.hxx>      // SwViewOptions
41 #include <paratr.hxx>       // SwFmtDrop
42 #include <txtcfg.hxx>
43 #include <itrform2.hxx>
44 #include <porrst.hxx>
45 #include <portab.hxx>       // pLastTab->
46 #include <porfly.hxx>       // CalcFlyWidth
47 #include <portox.hxx>       // WhichTxtPortion
48 #include <porref.hxx>       // WhichTxtPortion
49 #include <porfld.hxx>       // SwNumberPortion fuer CalcAscent()
50 #include <porftn.hxx>       // SwFtnPortion
51 #include <porhyph.hxx>
52 #include <guess.hxx>
53 #include <blink.hxx>        // pBlink
54 #include <ftnfrm.hxx>       // WhichFirstPortion() -> mal Verlagern.
55 #include <redlnitr.hxx>     // SwRedlineItr
56 #include <pagefrm.hxx>
57 #include <pagedesc.hxx> // SwPageDesc
58 #include <tgrditem.hxx>
59 #include <doc.hxx>          // SwDoc
60 #include <pormulti.hxx>     // SwMultiPortion
61 #define _SVSTDARR_LONGS
62 #include <svl/svstdarr.hxx>
63 #include <unotools/charclass.hxx>
64 
65 #if OSL_DEBUG_LEVEL > 1
66 #include <ndtxt.hxx>        // pSwpHints, Ausgabeoperator
67 #endif
68 
69 using namespace ::com::sun::star;
70 
71 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
72 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos );
73 
74 #define MAX_TXTPORLEN 300
75 
76 inline void ClearFly( SwTxtFormatInfo &rInf )
77 {
78     if( rInf.GetFly() )
79     {
80         delete rInf.GetFly();
81         rInf.SetFly(0);
82     }
83 }
84 
85 /*************************************************************************
86  *                  SwTxtFormatter::CtorInitTxtFormatter()
87  *************************************************************************/
88 
89 void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf )
90 {
91     CtorInitTxtPainter( pNewFrm, pNewInf );
92     pInf = pNewInf;
93     pDropFmt = GetInfo().GetDropFmt();
94     pMulti = NULL;
95 
96     bOnceMore = sal_False;
97     bFlyInCntBase = sal_False;
98     bChanges = sal_False;
99     bTruncLines = sal_False;
100     nCntEndHyph = 0;
101     nCntMidHyph = 0;
102     nLeftScanIdx = STRING_LEN;
103     nRightScanIdx = 0;
104     m_nHintEndIndex = 0;
105 
106     if( nStart > GetInfo().GetTxt().Len() )
107     {
108         ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" );
109         nStart = GetInfo().GetTxt().Len();
110     }
111 
112 }
113 
114 /*************************************************************************
115  *                      SwTxtFormatter::DTOR
116  *************************************************************************/
117 
118 SwTxtFormatter::~SwTxtFormatter()
119 {
120     // Auesserst unwahrscheinlich aber denkbar.
121     // z.B.: Feld spaltet sich auf, Widows schlagen zu
122     if( GetInfo().GetRest() )
123     {
124         delete GetInfo().GetRest();
125         GetInfo().SetRest(0);
126     }
127 }
128 
129 /*************************************************************************
130  *                      SwTxtFormatter::Insert()
131  *************************************************************************/
132 
133 void SwTxtFormatter::Insert( SwLineLayout *pLay )
134 {
135     // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element.
136     if ( pCurr )
137     {
138         pLay->SetNext( pCurr->GetNext() );
139         pCurr->SetNext( pLay );
140     }
141     else
142         pCurr = pLay;
143 }
144 
145 /*************************************************************************
146  *                  SwTxtFormatter::GetFrmRstHeight()
147  *************************************************************************/
148 
149 KSHORT SwTxtFormatter::GetFrmRstHeight() const
150 {
151     // 8725: Uns interessiert die Resthoehe bezogen auf die Seite.
152     // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht
153     // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn
154     // gerufen.
155     // Falsch: const SwFrm *pUpper = pFrm->GetUpper();
156     const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm();
157     const SwTwips nHeight = pPage->Frm().Top()
158                           + pPage->Prt().Top()
159                           + pPage->Prt().Height() - Y();
160     if( 0 > nHeight )
161         return pCurr->Height();
162     else
163         return KSHORT( nHeight );
164 }
165 
166 /*************************************************************************
167  *                  SwTxtFormatter::UnderFlow()
168  *************************************************************************/
169 
170 SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf )
171 {
172     // Werte sichern und rInf initialisieren.
173     SwLinePortion *pUnderFlow = rInf.GetUnderFlow();
174     if( !pUnderFlow )
175         return 0;
176 
177     // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der
178     // naechsten Zeile durchaus noch einmal drankommen koennen.
179     // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt.
180 
181     const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos();
182     const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos();
183 
184     // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF
185     // 3983: Nicht ClearFly(rInf) !
186     SwFlyPortion *pFly = rInf.GetFly();
187     rInf.SetFly( 0 );
188 
189     FeedInf( rInf );
190     rInf.SetLast( pCurr );
191     // pUnderFlow braucht nicht deletet werden, weil es im folgenden
192     // Truncate() untergehen wird.
193     rInf.SetUnderFlow(0);
194     rInf.SetSoftHyphPos( nSoftHyphPos );
195     rInf.SetUnderScorePos( nUnderScorePos );
196     rInf.SetPaintOfst( GetLeftMargin() );
197 
198     // Wir suchen die Portion mit der Unterlaufposition
199     SwLinePortion *pPor = pCurr->GetFirstPortion();
200     if( pPor != pUnderFlow )
201     {
202         // pPrev wird die letzte Portion vor pUnderFlow,
203         // die noch eine echte Breite hat.
204         // Ausnahme: SoftHyphPortions duerfen dabei natuerlich
205         // nicht vergessen werden, obwohl sie keine Breite haben.
206         SwLinePortion *pTmpPrev = pPor;
207         while( pPor && pPor != pUnderFlow )
208         {
209             DBG_LOOP;
210             if( !pPor->IsKernPortion() &&
211                 ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
212             {
213                 while( pTmpPrev != pPor )
214                 {
215                     pTmpPrev->Move( rInf );
216                     rInf.SetLast( pTmpPrev );
217                     pTmpPrev = pTmpPrev->GetPortion();
218                     ASSERT( pTmpPrev, "UnderFlow: Loosing control!" );
219                 };
220             }
221             pPor = pPor->GetPortion();
222         }
223         pPor = pTmpPrev;
224         if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen
225             ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
226               pPor->IsFlyCntPortion() ) )
227         {
228             pPor->Move( rInf );
229             rInf.SetLast( pPor );
230             rInf.SetStopUnderFlow( sal_True );
231             pPor = pUnderFlow;
232         }
233     }
234 
235     // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ?
236     ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" );
237 
238     // OD 2004-05-26 #i29529# - correction: no delete of footnotes
239 //    if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() )
240 //    {
241 //        SwLinePortion *pTmp = pPor->GetPortion();
242 //        while( pTmp )
243 //        {
244 //            if( pTmp->IsFtnPortion() )
245 //                ((SwFtnPortion*)pTmp)->ClearFtn();
246 //            pTmp = pTmp->GetPortion();
247 //        }
248 //    }
249 
250     /*-----------------14.12.94 09:45-------------------
251      * 9849: Schnellschuss
252      * --------------------------------------------------*/
253     if ( pPor==rInf.GetLast() )
254     {
255         // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich
256         // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber
257         // mehrere Zeilen geht und in der zweiten Zeile in einen Fly
258         // hineinlaeuft!
259         rInf.SetFly( pFly ); // wg. 28300
260         pPor->Truncate();
261         return pPor; // Reicht das?
262     }
263     /*---------------------------------------------------
264      * Ende des Schnellschusses wg. 9849
265      * --------------------------------------------------*/
266 
267     // 4656: X + Width == 0 bei SoftHyph > Zeile ?!
268     if( !pPor || !(rInf.X() + pPor->Width()) )
269     {
270         delete pFly;
271         return 0;
272     }
273 
274     // Vorbereitungen auf's Format()
275     // Wir muessen die Kette hinter pLast abknipsen, weil
276     // nach dem Format() ein Insert erfolgt.
277     SeekAndChg( rInf );
278 
279     // line width is adjusted, so that pPor does not fit to current
280     // line anymore
281     rInf.Width( (sal_uInt16)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
282     rInf.SetLen( pPor->GetLen() );
283     rInf.SetFull( sal_False );
284     if( pFly )
285     {
286         // Aus folgendem Grund muss die FlyPortion neu berechnet werden:
287         // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie
288         // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht,
289         // so hat die FlyPortion eine falsche Groesse/Fixsize.
290         rInf.SetFly( pFly );
291         CalcFlyWidth( rInf );
292     }
293     rInf.GetLast()->SetPortion(0);
294 
295     // Eine Ausnahme bildet das SwLineLayout, dass sich beim
296     // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg:
297     if( rInf.GetLast() == pCurr )
298     {
299         if( pPor->InTxtGrp() && !pPor->InExpGrp() )
300         {
301             MSHORT nOldWhich = pCurr->GetWhichPor();
302             *(SwLinePortion*)pCurr = *pPor;
303             pCurr->SetPortion( pPor->GetPortion() );
304             pCurr->SetWhichPor( nOldWhich );
305             pPor->SetPortion( 0 );
306             delete pPor;
307             pPor = pCurr;
308         }
309     }
310     pPor->Truncate();
311     SwLinePortion *const pRest( rInf.GetRest() );
312     if (pRest && pRest->InFldGrp() &&
313         static_cast<SwFldPortion*>(pRest)->IsNoLength())
314     {
315         // HACK: decrement again, so we pick up the suffix in next line!
316         --m_nHintEndIndex;
317     }
318     delete pRest;
319     rInf.SetRest(0);
320     return pPor;
321 }
322 
323 /*************************************************************************
324  *                      SwTxtFormatter::InsertPortion()
325  *************************************************************************/
326 
327 void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf,
328                                     SwLinePortion *pPor ) const
329 {
330     // Die neue Portion wird eingefuegt,
331     // bei dem LineLayout ist allerdings alles anders...
332     if( pPor == pCurr )
333     {
334         if ( pCurr->GetPortion() )
335         {
336             pPor = pCurr->GetPortion();
337         }
338 
339         // --> OD 2010-07-07 #i112181#
340         rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
341         // <--
342     }
343     else
344     {
345         SwLinePortion *pLast = rInf.GetLast();
346         if( pLast->GetPortion() )
347         {
348             while( pLast->GetPortion() )
349                 pLast = pLast->GetPortion();
350             rInf.SetLast( pLast );
351         }
352         pLast->Insert( pPor );
353 
354         rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() );
355 
356         // Maxima anpassen:
357         if( pCurr->Height() < pPor->Height() )
358             pCurr->Height( pPor->Height() );
359         if( pCurr->GetAscent() < pPor->GetAscent() )
360             pCurr->SetAscent( pPor->GetAscent() );
361     }
362 
363     // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate)
364     rInf.SetLast( pPor );
365     while( pPor )
366     {
367         DBG_LOOP;
368         pPor->Move( rInf );
369         rInf.SetLast( pPor );
370         pPor = pPor->GetPortion();
371     }
372 }
373 
374 /*************************************************************************
375  *                      SwTxtFormatter::BuildPortion()
376  *************************************************************************/
377 
378 void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf )
379 {
380     ASSERT( rInf.GetTxt().Len() < STRING_LEN,
381             "SwTxtFormatter::BuildPortions: bad text length in info" );
382 
383     rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
384 
385     // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet.
386     // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt
387     // wird. In CalcAscent geschieht dies automatisch.
388     rInf.SetLast( pCurr );
389     rInf.ForcedLeftMargin( 0 );
390 
391     ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" );
392 
393     if( !pCurr->GetAscent() && !pCurr->Height() )
394         CalcAscent( rInf, pCurr );
395 
396     SeekAndChg( rInf );
397 
398     // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
399     ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" );
400     CalcFlyWidth( rInf );
401     SwFlyPortion *pFly = rInf.GetFly();
402     if( pFly )
403     {
404         if ( 0 < pFly->Fix() )
405             ClearFly( rInf );
406         else
407             rInf.SetFull(sal_True);
408     }
409 
410     SwLinePortion *pPor = NewPortion( rInf );
411 
412     // Asian grid stuff
413     GETGRID( pFrm->FindPageFrm() )
414     const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() &&
415                               GRID_LINES_CHARS == pGrid->GetGridType();
416 
417     const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
418     const sal_uInt16 nGridWidth = bHasGrid ?
419                                 GETGRIDWIDTH(pGrid,pDoc) : 0;   //for textgrid refactor
420 
421     // used for grid mode only:
422     // the pointer is stored, because after formatting of non-asian text,
423     // the width of the kerning portion has to be adjusted
424     SwKernPortion* pGridKernPortion = 0;
425 
426     sal_Bool bFull;
427     SwTwips nUnderLineStart = 0;
428     rInf.Y( Y() );
429 
430     while( pPor && !rInf.IsStop() )
431     {
432         ASSERT( rInf.GetLen() < STRING_LEN &&
433                 rInf.GetIdx() <= rInf.GetTxt().Len(),
434                 "SwTxtFormatter::BuildPortions: bad length in info" );
435         DBG_LOOP;
436 
437         // We have to check the script for fields in order to set the
438         // correct nActual value for the font.
439         if( pPor->InFldGrp() )
440             ((SwFldPortion*)pPor)->CheckScript( rInf );
441 
442         if( ! bHasGrid && rInf.HasScriptSpace() &&
443             rInf.GetLast() && rInf.GetLast()->InTxtGrp() &&
444             rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
445         {
446             sal_uInt8 nNxtActual = rInf.GetFont()->GetActual();
447             sal_uInt8 nLstActual = nNxtActual;
448             sal_uInt16 nLstHeight = (sal_uInt16)rInf.GetFont()->GetHeight();
449             sal_Bool bAllowBefore = sal_False;
450             sal_Bool bAllowBehind = sal_False;
451             const CharClass& rCC = GetAppCharClass();
452 
453             // are there any punctuation characters on both sides
454             // of the kerning portion?
455             if ( pPor->InFldGrp() )
456             {
457                 XubString aAltTxt;
458                 if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) &&
459                         aAltTxt.Len() )
460                 {
461                     bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 );
462 
463                     const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont();
464                     if ( pTmpFnt )
465                         nNxtActual = pTmpFnt->GetActual();
466                 }
467             }
468             else
469                 bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() );
470 
471             const SwLinePortion* pLast = rInf.GetLast();
472             if ( bAllowBehind && pLast )
473             {
474                 if ( pLast->InFldGrp() )
475                 {
476                     XubString aAltTxt;
477                     if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) &&
478                          aAltTxt.Len() )
479                     {
480                         bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 );
481 
482                         const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont();
483                         if ( pTmpFnt )
484                         {
485                             nLstActual = pTmpFnt->GetActual();
486                             nLstHeight = (sal_uInt16)pTmpFnt->GetHeight();
487                         }
488                     }
489                 }
490                 else if ( rInf.GetIdx() )
491                 {
492                     bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 );
493                     // Note: ScriptType returns values in [1,4]
494                     if ( bAllowBefore )
495                         nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1;
496                 }
497 
498                 nLstHeight /= 5;
499                 // does the kerning portion still fit into the line?
500                 if( bAllowBefore && ( nLstActual != nNxtActual ) &&
501                     nLstHeight && rInf.X() + nLstHeight <= rInf.Width() )
502                 {
503                     SwKernPortion* pKrn =
504                         new SwKernPortion( *rInf.GetLast(), nLstHeight,
505                                            pLast->InFldGrp() && pPor->InFldGrp() );
506                     rInf.GetLast()->SetPortion( NULL );
507                     InsertPortion( rInf, pKrn );
508                 }
509             }
510         }
511         else if ( bHasGrid && ! pGridKernPortion && ! pMulti )
512         {
513             // insert a grid kerning portion
514             if ( ! pGridKernPortion )
515                 pGridKernPortion = pPor->IsKernPortion() ?
516                                    (SwKernPortion*)pPor :
517                                    new SwKernPortion( *pCurr );
518 
519             // if we have a new GridKernPortion, we initially calculate
520             // its size so that its ends on the grid
521             const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
522             const SwLayoutFrm* pBody = pPageFrm->FindBodyCont();
523             SWRECTFN( pPageFrm )
524 
525             const long nGridOrigin = pBody ?
526                                     (pBody->*fnRect->fnGetPrtLeft)() :
527                                     (pPageFrm->*fnRect->fnGetPrtLeft)();
528 
529             SwTwips nStartX = rInf.X() + GetLeftMargin();
530             if ( bVert )
531             {
532                 Point aPoint( nStartX, 0 );
533                 pFrm->SwitchHorizontalToVertical( aPoint );
534                 nStartX = aPoint.Y();
535             }
536 
537             const SwTwips nOfst = nStartX - nGridOrigin;
538             if ( nOfst )
539             {
540                 const sal_uLong i = ( nOfst > 0 ) ?
541                                 ( ( nOfst - 1 ) / nGridWidth + 1 ) :
542                                 0;
543                 const SwTwips nKernWidth = i * nGridWidth - nOfst;
544                 const SwTwips nRestWidth = rInf.Width() - rInf.X();
545 
546                 if ( nKernWidth <= nRestWidth )
547                     pGridKernPortion->Width( (sal_uInt16)nKernWidth );
548             }
549 
550             if ( pGridKernPortion != pPor )
551                 InsertPortion( rInf, pGridKernPortion );
552         }
553 
554         // the multi-portion has it's own format function
555         if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) )
556             bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) );
557         else
558             bFull = pPor->Format( rInf );
559 
560         if( rInf.IsRuby() && !rInf.GetRest() )
561             bFull = sal_True;
562 
563         // if we are underlined, we store the beginning of this underlined
564         // segment for repaint optimization
565         if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart )
566             nUnderLineStart = GetLeftMargin() + rInf.X();
567 
568         if ( pPor->IsFlyPortion() )
569             pCurr->SetFly( sal_True );
570         // some special cases, where we have to take care for the repaint
571         // offset:
572         // 1. Underlined portions due to special underline feature
573         // 2. Right Tab
574         // 3. BidiPortions
575         // 4. other Multiportions
576         // 5. DropCaps
577         // 6. Grid Mode
578         else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
579                   // 1. Underlined portions
580                   nUnderLineStart &&
581                      // reformat is at end of an underlined portion and next portion
582                      // is not underlined
583                   ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
584                       UNDERLINE_NONE == pFnt->GetUnderline()
585                     ) ||
586                      // reformat is inside portion and portion is underlined
587                     ( rInf.GetReformatStart() >= rInf.GetIdx() &&
588                       rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
589                       UNDERLINE_NONE != pFnt->GetUnderline() ) ) )
590             rInf.SetPaintOfst( nUnderLineStart );
591         else if (  ! rInf.GetPaintOfst() &&
592                    // 2. Right Tab
593                    ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
594                    // 3. BidiPortions
595                      ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) ||
596                    // 4. Multi Portion and 5. Drop Caps
597                      ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
598                        rInf.GetReformatStart() >= rInf.GetIdx() &&
599                        rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
600                    // 6. Grid Mode
601                      || ( bHasGrid && SW_CJK != pFnt->GetActual() )
602                    )
603                 )
604             // we store the beginning of the critical portion as our
605             // paint offset
606             rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
607 
608         // under one of these conditions we are allowed to delete the
609         // start of the underline portion
610         if ( IsUnderlineBreak( *pPor, *pFnt ) )
611             nUnderLineStart = 0;
612 
613         if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
614             ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) )
615             SetFlyInCntBase();
616         // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim
617         //       naechsten Softhyphen wieder umgebrochen!
618         if ( !bFull )
619         {
620             rInf.ClrUnderFlow();
621             if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() &&
622                 pPor->GetLen() && !pPor->InFldGrp() )
623             {
624                 // The distance between two different scripts is set
625                 // to 20% of the fontheight.
626                 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
627                 if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) &&
628                     nTmp != rInf.GetTxt().Len() )
629                 {
630                     sal_uInt16 nDist = (sal_uInt16)(rInf.GetFont()->GetHeight()/5);
631 
632                     if( nDist )
633                     {
634                         // we do not want a kerning portion if any end
635                         // would be a punctuation character
636                         const CharClass& rCC = GetAppCharClass();
637                         if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) &&
638                              rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) )
639                         {
640                             // does the kerning portion still fit into the line?
641                             if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
642                                 new SwKernPortion( *pPor, nDist );
643                             else
644                                 bFull = sal_True;
645                         }
646                     }
647                 }
648             }
649         }
650 
651         if ( bHasGrid && pPor != pGridKernPortion && ! pMulti )
652         {
653             xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen();
654             const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
655 
656             const sal_uInt8 nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
657             const sal_uInt8 nNextScript = nTmp >= rInf.GetTxt().Len() ?
658                                      SW_CJK :
659                                      SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo );
660 
661             // snap non-asian text to grid if next portion is ASIAN or
662             // there are no more portions in this line
663             // be careful when handling an underflow event: the gridkernportion
664             // could have been deleted
665             if ( nRestWidth > 0 && SW_CJK != nCurrScript &&
666                 ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) )
667             {
668                 ASSERT( pGridKernPortion, "No GridKernPortion available" )
669 
670                 // calculate size
671                 SwLinePortion* pTmpPor = pGridKernPortion->GetPortion();
672                 sal_uInt16 nSumWidth = pPor->Width();
673                 while ( pTmpPor )
674                 {
675                     nSumWidth = nSumWidth + pTmpPor->Width();
676                     pTmpPor = pTmpPor->GetPortion();
677                 }
678 
679                 const sal_uInt16 i = nSumWidth ?
680                                  ( nSumWidth - 1 ) / nGridWidth + 1 :
681                                  0;
682                 const SwTwips nTmpWidth = i * nGridWidth;
683                 const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth),
684                                                 nRestWidth );
685                 const sal_uInt16 nKernWidth_1 = (sal_uInt16)(nKernWidth / 2);
686 
687                 ASSERT( nKernWidth <= nRestWidth,
688                         "Not enough space left for adjusting non-asian text in grid mode" )
689 
690                 pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
691                 rInf.X( rInf.X() + nKernWidth_1 );
692 
693                 if ( ! bFull )
694                     new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1),
695                                        sal_False, sal_True );
696 
697                 pGridKernPortion = 0;
698             }
699             else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
700                       pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
701                       pPor->InFldGrp() || nCurrScript != nNextScript )
702                 // next portion should snap to grid
703                 pGridKernPortion = 0;
704         }
705 
706         rInf.SetFull( bFull );
707 
708         // Restportions von mehrzeiligen Feldern haben bisher noch
709         // nicht den richtigen Ascent.
710         if ( !pPor->GetLen() && !pPor->IsFlyPortion()
711             && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
712             && !pPor->IsMultiPortion() )
713             CalcAscent( rInf, pPor );
714 
715         InsertPortion( rInf, pPor );
716         pPor = NewPortion( rInf );
717     }
718 
719     if( !rInf.IsStop() )
720     {
721         // der letzte rechte, zentrierte, dezimale Tab
722         SwTabPortion *pLastTab = rInf.GetLastTab();
723         if( pLastTab )
724             pLastTab->FormatEOL( rInf );
725         else if( rInf.GetLast() && rInf.LastKernPortion() )
726             rInf.GetLast()->FormatEOL( rInf );
727     }
728     if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp()
729         && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() )
730         rInf.SetNumDone( sal_False );
731 
732     // 3260, 3860: Fly auf jeden Fall loeschen!
733     ClearFly( rInf );
734 }
735 
736 /*************************************************************************
737  *                 SwTxtFormatter::CalcAdjustLine()
738  *************************************************************************/
739 
740 void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
741 {
742     if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti)
743     {
744         pCurrent->SetFormatAdj(sal_True);
745         if( IsFlyInCntBase() )
746         {
747             CalcAdjLine( pCurrent );
748             // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint
749             // auf jeden Fall umsetzen, deshalb bAllWays = sal_True
750             UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True );
751         }
752     }
753 }
754 
755 /*************************************************************************
756  *                      SwTxtFormatter::CalcAscent()
757  *************************************************************************/
758 
759 void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor )
760 {
761     if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() )
762     {
763         // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten,
764         // dann ist ihre Groesse unabhaengig von harten Attributierungen.
765         SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt;
766         SwFontSave aSave( rInf, pFldFnt );
767         ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
768         ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) );
769     }
770     // --> OD 2008-06-05 #i89179#
771     // tab portion representing the list tab of a list label gets the
772     // same height and ascent as the corresponding number portion
773     else if ( pPor->InTabGrp() && pPor->GetLen() == 0 &&
774               rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
775               static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
776     {
777         const SwLinePortion* pLast = rInf.GetLast();
778         pPor->Height( pLast->Height() );
779         pPor->SetAscent( pLast->GetAscent() );
780     }
781     // <--
782     else
783     {
784         const SwLinePortion *pLast = rInf.GetLast();
785         sal_Bool bChg;
786 
787         // Fallunterscheidung: in leeren Zeilen werden die Attribute
788         // per SeekStart angeschaltet.
789         const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
790         if ( pPor->IsQuoVadisPortion() )
791             bChg = SeekStartAndChg( rInf, sal_True );
792         else
793         {
794             if( bFirstPor )
795             {
796                 if( rInf.GetTxt().Len() )
797                 {
798                     if ( pPor->GetLen() || !rInf.GetIdx()
799                          || ( pCurr != pLast && !pLast->IsFlyPortion() )
800                          || !pCurr->IsRest() ) // statt !rInf.GetRest()
801                         bChg = SeekAndChg( rInf );
802                     else
803                         bChg = SeekAndChgBefore( rInf );
804                 }
805                 else if ( pMulti )
806                     // do not open attributes starting at 0 in empty multi
807                     // portions (rotated numbering followed by a footnote
808                     // can cause trouble, because the footnote attribute
809                     // starts at 0, but if we open it, the attribute handler
810                     // cannot handle it.
811                     bChg = sal_False;
812                 else
813                     bChg = SeekStartAndChg( rInf );
814             }
815             else
816                 bChg = SeekAndChg( rInf );
817         }
818         if( bChg || bFirstPor || !pPor->GetAscent()
819             || !rInf.GetLast()->InTxtGrp() )
820         {
821             pPor->SetAscent( rInf.GetAscent()  );
822             pPor->Height( rInf.GetTxtHeight() );
823         }
824         else
825         {
826             pPor->Height( pLast->Height() );
827             pPor->SetAscent( pLast->GetAscent() );
828         }
829     }
830 }
831 
832 /*************************************************************************
833  *                      class SwMetaPortion
834  *************************************************************************/
835 
836 class SwMetaPortion : public SwTxtPortion
837 {
838 public:
839     inline  SwMetaPortion() { SetWhichPor( POR_META ); }
840     virtual void Paint( const SwTxtPaintInfo &rInf ) const;
841 //    OUTPUT_OPERATOR
842 };
843 
844 //CLASSIO( SwMetaPortion )
845 
846 /*************************************************************************
847  *               virtual SwMetaPortion::Paint()
848  *************************************************************************/
849 
850 void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const
851 {
852     if ( Width() )
853     {
854         rInf.DrawViewOpt( *this, POR_META );
855         SwTxtPortion::Paint( rInf );
856     }
857 }
858 
859 
860 /*************************************************************************
861  *                      SwTxtFormatter::WhichTxtPor()
862  *************************************************************************/
863 
864 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
865 {
866     SwTxtPortion *pPor = 0;
867     if( GetFnt()->IsTox() )
868         pPor = new SwToxPortion;
869     else
870     {
871         if( GetFnt()->IsRef() )
872             pPor = new SwRefPortion;
873         else if (GetFnt()->IsMeta())
874         {
875             pPor = new SwMetaPortion;
876         }
877         else
878         {
879             // Erst zum Schluss !
880             // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben,
881              // z.B. bei nicht darstellbaren Zeichen.
882             if( rInf.GetLen() > 0 )
883             {
884                 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART )
885                     pPor = new SwFieldMarkPortion();
886                 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND )
887                     pPor = new SwFieldMarkPortion();
888                 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT )
889                     pPor = new SwFieldFormPortion();
890             }
891             if( !pPor )
892             {
893                 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
894                     pPor = pCurr;
895                 else
896                 {
897                     pPor = new SwTxtPortion;
898                     if( GetFnt()->IsURL() )
899                         pPor->SetWhichPor( POR_URL );
900                 }
901             }
902         }
903     }
904     return pPor;
905 }
906 
907 /*************************************************************************
908  *                      SwTxtFormatter::NewTxtPortion()
909  *************************************************************************/
910 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert:
911 // 1) Tabs
912 // 2) Linebreaks
913 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
914 // 4) naechster Attributwechsel
915 
916 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
917 {
918     // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr
919     // Wenn pCurr nicht von SwTxtPortion abgeleitet ist,
920     // muessen wir duplizieren ...
921     Seek( rInf.GetIdx() );
922     SwTxtPortion *pPor = WhichTxtPor( rInf );
923 
924     // until next attribute change:
925     const xub_StrLen nNextAttr = GetNextAttr();
926     xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() );
927 
928     // end of script type:
929     const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
930     nNextChg = Min( nNextChg, nNextScript );
931 
932     // end of direction:
933     const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
934     nNextChg = Min( nNextChg, nNextDir );
935 
936     // 7515, 7516, 3470, 6441 : Turbo-Boost
937     // Es wird unterstellt, dass die Buchstaben eines Fonts nicht
938     // groesser als doppelt so breit wie hoch sind.
939     // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen.
940     // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe
941     // ergibt sich erst im CalcAscent!
942     // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times
943     // New Roman besitzt einen Ascent von 182, eine Hoehe von 200
944     // und eine Breite von 53! Daraus folgt, dass eine Zeile mit
945     // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von
946     // Faktor 2 auf 8 (wg. negativen Kernings).
947 
948     pPor->SetLen(1);
949     CalcAscent( rInf, pPor );
950 
951     const SwFont* pTmpFnt = rInf.GetFont();
952     KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ),
953                           KSHORT( pPor->GetAscent() ) ) / 8;
954     if ( !nExpect )
955         nExpect = 1;
956     nExpect = (sal_uInt16)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect));
957     if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
958         nNextChg = Min( nExpect, rInf.GetTxt().Len() );
959 
960     // we keep an invariant during method calls:
961     // there are no portion ending characters like hard spaces
962     // or tabs in [ nLeftScanIdx, nRightScanIdx ]
963     if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
964     {
965         if ( nNextChg > nRightScanIdx )
966             nNextChg = nRightScanIdx =
967                 rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
968     }
969     else
970     {
971         nLeftScanIdx = rInf.GetIdx();
972         nNextChg = nRightScanIdx =
973                 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
974     }
975 
976     pPor->SetLen( nNextChg - rInf.GetIdx() );
977     rInf.SetLen( pPor->GetLen() );
978     return pPor;
979 }
980 
981 
982 /*************************************************************************
983  *                 SwTxtFormatter::WhichFirstPortion()
984  *************************************************************************/
985 
986 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
987 {
988     SwLinePortion *pPor = 0;
989 
990     if( rInf.GetRest() )
991     {
992         // 5010: Tabs und Felder
993         if( '\0' != rInf.GetHookChar() )
994             return 0;
995 
996         pPor = rInf.GetRest();
997         if( pPor->IsErgoSumPortion() )
998             rInf.SetErgoDone(sal_True);
999         else
1000             if( pPor->IsFtnNumPortion() )
1001                 rInf.SetFtnDone(sal_True);
1002             else
1003                 if( pPor->InNumberGrp() )
1004                     rInf.SetNumDone(sal_True);
1005         if( pPor )
1006         {
1007             rInf.SetRest(0);
1008             pCurr->SetRest( sal_True );
1009             return pPor;
1010         }
1011     }
1012 
1013     // ???? und ????: im Follow duerfen wir schon stehen,
1014     // entscheidend ist, ob pFrm->GetOfst() == 0 ist!
1015     if( rInf.GetIdx() )
1016     {
1017         // Nun koennen auch FtnPortions und ErgoSumPortions
1018         // verlaengert werden.
1019 
1020         // 1) Die ErgoSumTexte
1021         if( !rInf.IsErgoDone() )
1022         {
1023             if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1024                 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1025             rInf.SetErgoDone( sal_True );
1026         }
1027 
1028         // 2) Arrow portions
1029         if( !pPor && !rInf.IsArrowDone() )
1030         {
1031             if( pFrm->GetOfst() && !pFrm->IsFollow() &&
1032                 rInf.GetIdx() == pFrm->GetOfst() )
1033                 pPor = new SwArrowPortion( *pCurr );
1034             rInf.SetArrowDone( sal_True );
1035         }
1036 
1037         // 3) Kerning portions at beginning of line in grid mode
1038         if ( ! pPor && ! pCurr->GetPortion() )
1039         {
1040             GETGRID( GetTxtFrm()->FindPageFrm() )
1041             if ( pGrid )
1042                 pPor = new SwKernPortion( *pCurr );
1043         }
1044 
1045         // 4) Die Zeilenreste (mehrzeilige Felder)
1046         if( !pPor )
1047         {
1048             pPor = rInf.GetRest();
1049             // 6922: Nur bei pPor natuerlich.
1050             if( pPor )
1051             {
1052                 pCurr->SetRest( sal_True );
1053                 rInf.SetRest(0);
1054             }
1055         }
1056     }
1057     else
1058     {
1059         // 5) Die Fussnotenzahlen
1060         if( !rInf.IsFtnDone() )
1061         {
1062             ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1063                      "Rotated number portion trouble" )
1064 
1065             sal_Bool bFtnNum = pFrm->IsFtnNumFrm();
1066             rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1067             if( bFtnNum )
1068                 pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1069             rInf.SetFtnDone( sal_True );
1070         }
1071 
1072         // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster,
1073         // entscheidend ist, ob der SwFtnFrm ein Follow ist.
1074         if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1075         {
1076             if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1077                 pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1078             rInf.SetErgoDone( sal_True );
1079         }
1080 
1081         // 7) Die Numerierungen
1082         if( !rInf.IsNumDone() && !pPor )
1083         {
1084             ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1085                      "Rotated number portion trouble" )
1086 
1087             // Wenn wir im Follow stehen, dann natuerlich nicht.
1088             if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1089                 pPor = (SwLinePortion*)NewNumberPortion( rInf );
1090             rInf.SetNumDone( sal_True );
1091         }
1092         // 8) Die DropCaps
1093         if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1094             pPor = (SwLinePortion*)NewDropPortion( rInf );
1095 
1096         // 9) Kerning portions at beginning of line in grid mode
1097         if ( !pPor && !pCurr->GetPortion() )
1098         {
1099             GETGRID( GetTxtFrm()->FindPageFrm() )
1100             if ( pGrid )
1101                 pPor = new SwKernPortion( *pCurr );
1102         }
1103     }
1104 
1105         // 10) Decimal tab portion at the beginning of each line in table cells
1106         if ( !pPor && !pCurr->GetPortion() &&
1107              GetTxtFrm()->IsInTab() &&
1108              GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1109         {
1110             pPor = NewTabPortion( rInf, true );
1111         }
1112 
1113         // 11) suffix of meta-field
1114         if (!pPor)
1115         {
1116             pPor = TryNewNoLengthPortion(rInf);
1117         }
1118 
1119     return pPor;
1120 }
1121 
1122 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1123 {
1124     if( !pCurr->GetNext() )
1125         return sal_False;
1126     const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1127     sal_Bool bRet = sal_False;
1128     while( pPor && !bRet )
1129     {
1130         bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1131             (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1132         if( !pPor->GetLen() )
1133             break;
1134         pPor = pPor->GetPortion();
1135     }
1136     return bRet;
1137 }
1138 
1139 /*************************************************************************
1140  *                      SwTxtFormatter::NewPortion()
1141  *************************************************************************/
1142 
1143 /* NewPortion stellt rInf.nLen ein.
1144  * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr,
1145  * attrwechsel.
1146  * Drei Faelle koennen eintreten:
1147  * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert
1148  *    -> return 0;
1149  * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert
1150  *    -> Breite neu einstellen und return new FlyPortion
1151  * 3) Es muss eine neue Portion gebaut werden.
1152  *    -> CalcFlyWidth emuliert ggf. die Breite und return Portion
1153  */
1154 
1155 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1156 {
1157     // Underflow hat Vorrang
1158     rInf.SetStopUnderFlow( sal_False );
1159     if( rInf.GetUnderFlow() )
1160     {
1161         ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1162         return UnderFlow( rInf );
1163     }
1164 
1165     // Wenn die Zeile voll ist, koennten noch Flys oder
1166     // UnderFlow-LinePortions warten ...
1167     if( rInf.IsFull() )
1168     {
1169         // ????: LineBreaks und Flys (bug05.sdw)
1170         // 8450: IsDummy()
1171         if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1172             return 0;
1173 
1174         // Wenn der Text an den Fly gestossen ist, oder wenn
1175         // der Fly als erstes drankommt, weil er ueber dem linken
1176         // Rand haengt, wird GetFly() returnt.
1177         // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1178         // naturgemaesz eine 0.
1179         if( rInf.GetFly() )
1180         {
1181             if( rInf.GetLast()->IsBreakPortion() )
1182             {
1183                 delete rInf.GetFly();
1184                 rInf.SetFly( 0 );
1185             }
1186 
1187             return rInf.GetFly();
1188         }
1189         // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1190         // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1191         // bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1192         // (die Textmasse wurde ja durchformatiert).
1193         if( rInf.GetRest() )
1194             rInf.SetNewLine( sal_True );
1195         else
1196         {
1197             // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1198             // jetzt aber kein Rest mehr anliegt,
1199             // muss sie auf jeden Fall neu formatiert werden!
1200             if( lcl_OldFieldRest( GetCurr() ) )
1201                 rInf.SetNewLine( sal_True );
1202             else
1203             {
1204                 SwLinePortion *pFirst = WhichFirstPortion( rInf );
1205                 if( pFirst )
1206                 {
1207                     rInf.SetNewLine( sal_True );
1208                     if( pFirst->InNumberGrp() )
1209                         rInf.SetNumDone( sal_False) ;
1210                     delete pFirst;
1211                 }
1212             }
1213         }
1214 
1215         return 0;
1216     }
1217 
1218     SwLinePortion *pPor = WhichFirstPortion( rInf );
1219 
1220     // Check for Hidden Portion:
1221     if ( !pPor )
1222     {
1223         xub_StrLen nEnd = rInf.GetIdx();
1224         if ( lcl_BuildHiddenPortion( rInf, nEnd ) )
1225             pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1226     }
1227 
1228     if( !pPor )
1229     {
1230         if( ( !pMulti || pMulti->IsBidi() ) &&
1231             // --> FME 2005-02-14 #i42734#
1232             // No multi portion if there is a hook character waiting:
1233             ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1234             // <--
1235         {
1236             // We open a multiportion part, if we enter a multi-line part
1237             // of the paragraph.
1238             xub_StrLen nEnd = rInf.GetIdx();
1239             SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1240             if( pCreate )
1241             {
1242                 SwMultiPortion* pTmp = NULL;
1243 
1244                 if ( SW_MC_BIDI == pCreate->nId )
1245                     pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1246                 else if ( SW_MC_RUBY == pCreate->nId )
1247                 {
1248                     Seek( rInf.GetIdx() );
1249                     sal_Bool bRubyTop;
1250                     sal_Bool* pRubyPos = 0;
1251 
1252                     if ( rInf.SnapToGrid() )
1253                     {
1254                         GETGRID( GetTxtFrm()->FindPageFrm() )
1255                         if ( pGrid )
1256                         {
1257                             bRubyTop = ! pGrid->GetRubyTextBelow();
1258                             pRubyPos = &bRubyTop;
1259                         }
1260                     }
1261 
1262                     pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1263                                               *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1264                                               nEnd, 0, pRubyPos );
1265                 }
1266                 else if( SW_MC_ROTATE == pCreate->nId )
1267                     pTmp = new SwRotatedPortion( *pCreate, nEnd,
1268                                                  GetTxtFrm()->IsRightToLeft() );
1269                 else
1270                     pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1271 
1272                 delete pCreate;
1273                 CalcFlyWidth( rInf );
1274 
1275                 return pTmp;
1276             }
1277         }
1278         // 5010: Tabs und Felder
1279         xub_Unicode cChar = rInf.GetHookChar();
1280 
1281         if( cChar )
1282         {
1283             /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1284              * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1285              * gewandert ist ( so geschehen hinter Rahmen ).
1286              * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1287              * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1288              * DezimalTabs und Feldern (22615)
1289             */
1290             if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1291                 cChar = rInf.GetChar( rInf.GetIdx() );
1292             rInf.ClearHookChar();
1293         }
1294         else
1295         {
1296             if( rInf.GetIdx() >= rInf.GetTxt().Len() )
1297             {
1298                 rInf.SetFull(sal_True);
1299                 CalcFlyWidth( rInf );
1300                 return pPor;
1301             }
1302             cChar = rInf.GetChar( rInf.GetIdx() );
1303         }
1304 
1305         switch( cChar )
1306         {
1307             case CH_TAB:
1308                 pPor = NewTabPortion( rInf, false ); break;
1309 
1310             case CH_BREAK:
1311                 pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1312 
1313             case CHAR_SOFTHYPHEN:                   // soft hyphen
1314                 pPor = new SwSoftHyphPortion; break;
1315 
1316             case CHAR_HARDBLANK:                    // no-break space
1317                 pPor = new SwBlankPortion( ' ' ); break;
1318 
1319             case CHAR_HARDHYPHEN:               // non-breaking hyphen
1320                 pPor = new SwBlankPortion( '-' ); break;
1321 
1322             case CHAR_ZWSP:                     // zero width space
1323             case CHAR_ZWNBSP :                  // word joiner
1324 //            case CHAR_RLM :                     // right to left mark
1325 //            case CHAR_LRM :                     // left to right mark
1326                 pPor = new SwControlCharPortion( cChar ); break;
1327 
1328             case CH_TXTATR_BREAKWORD:
1329             case CH_TXTATR_INWORD:
1330                             if( rInf.HasHint( rInf.GetIdx() ) )
1331                             {
1332                                 pPor = NewExtraPortion( rInf );
1333                                 break;
1334                             }
1335                             // No break
1336             default        :
1337             {
1338                 SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1339                 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1340                 {
1341                     // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full:
1342                     // We have a decimal tab portion in the line and the next character has to be
1343                     // aligned at the tab stop position. We store the width from the beginning of
1344                     // the tab stop portion up to the portion containint the decimal separator:
1345                   if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1346                          POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1347                     {
1348                         ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" )
1349                         const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() );
1350                         static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1351                         rInf.SetTabDecimal( 0 );
1352                     }
1353                     // <--
1354                     else
1355                         rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1356                 }
1357 
1358                 if( rInf.GetRest() )
1359                 {
1360                     if( rInf.IsFull() )
1361                     {
1362                         rInf.SetNewLine(sal_True);
1363                         return 0;
1364                     }
1365                     pPor = rInf.GetRest();
1366                     rInf.SetRest(0);
1367                 }
1368                 else
1369                 {
1370                     if( rInf.IsFull() )
1371                         return 0;
1372                     pPor = NewTxtPortion( rInf );
1373                 }
1374                 break;
1375             }
1376         }
1377 
1378         // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht,
1379         // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet
1380         // hat, weil z.B. ein Tab enthalten ist.
1381         if( pPor && rInf.GetRest() )
1382             pPor->SetLen( 0 );
1383 
1384         // robust:
1385         if( !pPor || rInf.IsStop() )
1386         {
1387             delete pPor;
1388             return 0;
1389         }
1390     }
1391 
1392     // Special portions containing numbers (footnote anchor, footnote number,
1393     // numbering) can be contained in a rotated portion, if the user
1394     // choose a rotated character attribute.
1395     if ( pPor && ! pMulti )
1396     {
1397         if ( pPor->IsFtnPortion() )
1398         {
1399             const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1400 
1401             if ( pTxtFtn )
1402             {
1403                 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1404                 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1405                 const SwEndNoteInfo* pInfo;
1406                 if( rFtn.IsEndNote() )
1407                     pInfo = &pDoc->GetEndNoteInfo();
1408                 else
1409                     pInfo = &pDoc->GetFtnInfo();
1410                 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1411 
1412                 const SfxPoolItem* pItem;
1413                 sal_uInt16 nDir = 0;
1414                 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1415                     sal_True, &pItem ))
1416                     nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1417 
1418                 if ( 0 != nDir )
1419                 {
1420                     delete pPor;
1421                     pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1422                                                     DIR_BOTTOM2TOP :
1423                                                     DIR_TOP2BOTTOM );
1424                 }
1425             }
1426         }
1427         else if ( pPor->InNumberGrp() )
1428         {
1429             const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1430 
1431             if ( pNumFnt )
1432             {
1433                 sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1434                 if ( 0 != nDir )
1435                 {
1436                     delete pPor;
1437                     pPor = new SwRotatedPortion( 0, 900 == nDir ?
1438                                                     DIR_BOTTOM2TOP :
1439                                                     DIR_TOP2BOTTOM );
1440 
1441                     rInf.SetNumDone( sal_False );
1442                     rInf.SetFtnDone( sal_False );
1443                 }
1444             }
1445         }
1446     }
1447 
1448     // Der Font wird im Outputdevice eingestellt,
1449     // der Ascent und die Hoehe werden berechnet.
1450     if( !pPor->GetAscent() && !pPor->Height() )
1451         CalcAscent( rInf, pPor );
1452     rInf.SetLen( pPor->GetLen() );
1453 
1454     // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1455     CalcFlyWidth( rInf );
1456 
1457     // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1458     // Werte bereithalten muss:
1459     if( !pCurr->Height() )
1460     {
1461         ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1462         pCurr->Height( pPor->Height() );
1463         pCurr->SetAscent( pPor->GetAscent() );
1464     }
1465 
1466     ASSERT( !pPor || pPor->Height(),
1467             "SwTxtFormatter::NewPortion: something went wrong");
1468     if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1469     {
1470         delete pPor;
1471         pPor = rInf.GetFly();
1472     }
1473     return pPor;
1474 }
1475 
1476 /*************************************************************************
1477  *                      SwTxtFormatter::FormatLine()
1478  *************************************************************************/
1479 
1480 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos )
1481 {
1482     ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1483             "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1484 
1485     // For the formatting routines, we set pOut to the reference device.
1486     SwHookOut aHook( GetInfo() );
1487     if( GetInfo().GetLen() < GetInfo().GetTxt().Len() )
1488         GetInfo().SetLen( GetInfo().GetTxt().Len() );
1489 
1490     sal_Bool bBuild = sal_True;
1491     SetFlyInCntBase( sal_False );
1492     GetInfo().SetLineHeight( 0 );
1493     GetInfo().SetLineNettoHeight( 0 );
1494 
1495     // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1496     // und auch bei geaendertem Ascent (Absenken der Grundlinie).
1497     const KSHORT nOldHeight = pCurr->Height();
1498     const KSHORT nOldAscent = pCurr->GetAscent();
1499 
1500     pCurr->SetEndHyph( sal_False );
1501     pCurr->SetMidHyph( sal_False );
1502 
1503     // fly positioning can make it necessary format a line several times
1504     // for this, we have to keep a copy of our rest portion
1505     SwLinePortion* pFld = GetInfo().GetRest();
1506     SwFldPortion* pSaveFld = 0;
1507 
1508     if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1509         pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) );
1510 
1511     // for an optimal repaint rectangle, we want to compare fly portions
1512     // before and after the BuildPortions call
1513     const sal_Bool bOptimizeRepaint = AllowRepaintOpt();
1514     const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen();
1515     SvLongs* pFlyStart = 0;
1516 
1517     // these are the conditions for a fly position comparison
1518     if ( bOptimizeRepaint && pCurr->IsFly() )
1519     {
1520         pFlyStart = new SvLongs;
1521         SwLinePortion* pPor = pCurr->GetFirstPortion();
1522         long nPOfst = 0;
1523         sal_uInt16 nCnt = 0;
1524 
1525         while ( pPor )
1526         {
1527             if ( pPor->IsFlyPortion() )
1528                 // insert start value of fly portion
1529                 pFlyStart->Insert( nPOfst, nCnt++ );
1530 
1531             nPOfst += pPor->Width();
1532             pPor = pPor->GetPortion();
1533         }
1534     }
1535 
1536     // Hier folgt bald die Unterlaufpruefung.
1537     while( bBuild )
1538     {
1539         GetInfo().SetFtnInside( sal_False );
1540         GetInfo().SetOtherThanFtnInside( sal_False );
1541 
1542         // These values must not be reset by FormatReset();
1543         sal_Bool bOldNumDone = GetInfo().IsNumDone();
1544         sal_Bool bOldArrowDone = GetInfo().IsArrowDone();
1545         sal_Bool bOldErgoDone = GetInfo().IsErgoDone();
1546 
1547         // besides other things, this sets the repaint offset to 0
1548         FormatReset( GetInfo() );
1549 
1550         GetInfo().SetNumDone( bOldNumDone );
1551         GetInfo().SetArrowDone( bOldArrowDone );
1552         GetInfo().SetErgoDone( bOldErgoDone );
1553 
1554         // build new portions for this line
1555         BuildPortions( GetInfo() );
1556 
1557         if( GetInfo().IsStop() )
1558         {
1559             pCurr->SetLen( 0 );
1560             pCurr->Height( GetFrmRstHeight() + 1 );
1561             pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1562             pCurr->Width(0);
1563             pCurr->Truncate();
1564             return nStartPos;
1565         }
1566         else if( GetInfo().IsDropInit() )
1567         {
1568             DropInit();
1569             GetInfo().SetDropInit( sal_False );
1570         }
1571 
1572         pCurr->CalcLine( *this, GetInfo() );
1573         CalcRealHeight( GetInfo().IsNewLine() );
1574 
1575         if ( IsFlyInCntBase() && !IsQuick() )
1576         {
1577             KSHORT nTmpAscent, nTmpHeight;
1578             CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1579             AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1580             pCurr->CalcLine( *this, GetInfo() );
1581             CalcRealHeight();
1582         }
1583 
1584         // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1585         if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1586         {
1587             pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1588             bBuild = sal_False;
1589         }
1590         else
1591         {
1592             bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow(GetInfo()) )
1593                      || GetInfo().CheckFtnPortion(pCurr);
1594             if( bBuild )
1595             {
1596                 GetInfo().SetNumDone( bOldNumDone );
1597                 GetInfo().ResetMaxWidthDiff();
1598 
1599                 // delete old rest
1600                 if ( GetInfo().GetRest() )
1601                 {
1602                     delete GetInfo().GetRest();
1603                     GetInfo().SetRest( 0 );
1604                 }
1605 
1606                 // set original rest portion
1607                 if ( pSaveFld )
1608                     GetInfo().SetRest( new SwFldPortion( *pSaveFld ) );
1609 
1610                 pCurr->SetLen( 0 );
1611                 pCurr->Width(0);
1612                 pCurr->Truncate();
1613             }
1614         }
1615     }
1616 
1617     // calculate optimal repaint rectangle
1618     if ( bOptimizeRepaint )
1619     {
1620         GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) );
1621         if ( pFlyStart )
1622             delete pFlyStart;
1623     }
1624     else
1625         // Special case: We do not allow an optimitation of the repaint
1626         // area, but during formatting the repaint offset is set to indicate
1627         // a maximum value for the offset. This value has to be reset:
1628         GetInfo().SetPaintOfst( 0 );
1629 
1630     // This corrects the start of the reformat range if something has
1631     // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1632     // will give us a wrong result if we have to reformat another line
1633     GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() );
1634 
1635     // delete master copy of rest portion
1636     if ( pSaveFld )
1637         delete pSaveFld;
1638 
1639     xub_StrLen nNewStart = nStartPos + pCurr->GetLen();
1640 
1641     // adjust text if kana compression is enabled
1642     if ( GetInfo().CompressLine() )
1643     {
1644         SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1645 
1646         // adjust repaint offset
1647         if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1648             GetInfo().SetPaintOfst( nRepaintOfst );
1649     }
1650 
1651     CalcAdjustLine( pCurr );
1652 
1653     if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1654     {
1655         SetFlyInCntBase();
1656         GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1657         // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1658         // auch formatiert werden.
1659         GetInfo().SetShift( sal_True );
1660     }
1661 
1662     if ( IsFlyInCntBase() && !IsQuick() )
1663         UpdatePos( pCurr, GetTopLeft(), GetStart() );
1664 
1665     return nNewStart;
1666 }
1667 
1668 /*************************************************************************
1669  *                      SwTxtFormatter::RecalcRealHeight()
1670  *************************************************************************/
1671 
1672 void SwTxtFormatter::RecalcRealHeight()
1673 {
1674     sal_Bool bMore = sal_True;
1675     while(bMore)
1676     {
1677         DBG_LOOP;
1678         CalcRealHeight();
1679         bMore = Next() != 0;
1680     }
1681 }
1682 
1683 /*************************************************************************
1684  *                    SwTxtFormatter::CalcRealHeight()
1685  *************************************************************************/
1686 
1687 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine )
1688 {
1689     KSHORT nLineHeight = pCurr->Height();
1690     pCurr->SetClipping( sal_False );
1691 
1692     GETGRID( pFrm->FindPageFrm() )
1693     if ( pGrid && GetInfo().SnapToGrid() )
1694     {
1695         const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
1696         const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
1697         const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow();
1698 
1699         nLineHeight = nGridWidth + nRubyHeight;
1700         sal_uInt16 nLineDist = nLineHeight;
1701 
1702         while ( pCurr->Height() > nLineHeight )
1703             nLineHeight = nLineHeight + nLineDist;
1704 
1705         KSHORT nAsc = pCurr->GetAscent() +
1706                       ( bRubyTop ?
1707                        ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1708                        ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1709 
1710         pCurr->Height( nLineHeight );
1711         pCurr->SetAscent( nAsc );
1712         pInf->GetParaPortion()->SetFixLineHeight();
1713 
1714         // we ignore any line spacing options except from ...
1715         const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1716         if ( ! IsParaLine() && pSpace &&
1717              SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1718         {
1719             sal_uLong nTmp = pSpace->GetPropLineSpace();
1720 
1721             if( nTmp < 100 )
1722                 nTmp = 100;
1723 
1724             nTmp *= nLineHeight;
1725             nLineHeight = (sal_uInt16)(nTmp / 100);
1726         }
1727 
1728         pCurr->SetRealHeight( nLineHeight );
1729         return;
1730     }
1731 
1732     // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1733     // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1734     // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1735     // Shift-Return), die das Register durchaus beachten soll.
1736     if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1737         GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) )
1738     {
1739         const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1740         if( pSpace )
1741         {
1742             switch( pSpace->GetLineSpaceRule() )
1743             {
1744                 case SVX_LINE_SPACE_AUTO:
1745                 break;
1746                 case SVX_LINE_SPACE_MIN:
1747                 {
1748                     if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) )
1749                         nLineHeight = pSpace->GetLineHeight();
1750                     break;
1751                 }
1752                 case SVX_LINE_SPACE_FIX:
1753                 {
1754                     nLineHeight = pSpace->GetLineHeight();
1755                     KSHORT nAsc = ( 4 * nLineHeight ) / 5;  // 80%
1756                     if( nAsc < pCurr->GetAscent() ||
1757                         nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1758                         pCurr->SetClipping( sal_True );
1759                     pCurr->Height( nLineHeight );
1760                     pCurr->SetAscent( nAsc );
1761                     pInf->GetParaPortion()->SetFixLineHeight();
1762                 }
1763                 break;
1764                 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
1765             }
1766             if( !IsParaLine() )
1767                 switch( pSpace->GetInterLineSpaceRule() )
1768                 {
1769                     case SVX_INTER_LINE_SPACE_OFF:
1770                     break;
1771                     case SVX_INTER_LINE_SPACE_PROP:
1772                     {
1773                         long nTmp = pSpace->GetPropLineSpace();
1774                         // 50% ist das Minimum, bei 0% schalten wir auf
1775                         // den Defaultwert 100% um ...
1776                         if( nTmp < 50 )
1777                             nTmp = nTmp ? 50 : 100;
1778 
1779                         nTmp *= nLineHeight;
1780                         nTmp /= 100;
1781                         if( !nTmp )
1782                             ++nTmp;
1783                         nLineHeight = (KSHORT)nTmp;
1784                         break;
1785                     }
1786                     case SVX_INTER_LINE_SPACE_FIX:
1787                     {
1788                         nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1789                         break;
1790                     }
1791                     default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
1792                 }
1793         }
1794 #if OSL_DEBUG_LEVEL > 1
1795         KSHORT nDummy = nLineHeight + 1;
1796         (void)nDummy;
1797 #endif
1798 
1799         if( IsRegisterOn() )
1800         {
1801             SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1802             SWRECTFN( pFrm )
1803             if ( bVert )
1804                 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1805             nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1806             KSHORT nDiff = KSHORT( nTmpY % RegDiff() );
1807             if( nDiff )
1808                 nLineHeight += RegDiff() - nDiff;
1809         }
1810     }
1811     pCurr->SetRealHeight( nLineHeight );
1812 }
1813 
1814 /*************************************************************************
1815  *                      SwTxtFormatter::FeedInf()
1816  *************************************************************************/
1817 
1818 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1819 {
1820     // 3260, 3860: Fly auf jeden Fall loeschen!
1821     ClearFly( rInf );
1822     rInf.Init();
1823 
1824     rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1825     rInf.SetRoot( pCurr );
1826     rInf.SetLineStart( nStart );
1827     rInf.SetIdx( nStart );
1828 
1829     // Handle overflows:
1830     // --> FME 2004-11-25 #i34348# Changed type from sal_uInt16 to SwTwips
1831     SwTwips nTmpLeft = Left();
1832     SwTwips nTmpRight = Right();
1833     SwTwips nTmpFirst = FirstLeft();
1834     // <--
1835 
1836     if ( nTmpLeft > USHRT_MAX ||
1837          nTmpRight > USHRT_MAX ||
1838          nTmpFirst > USHRT_MAX )
1839     {
1840         SWRECTFN( rInf.GetTxtFrm() )
1841         nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1842         nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1843         nTmpFirst = nTmpLeft;
1844     }
1845 
1846     rInf.Left(  nTmpLeft  );
1847     rInf.Right( nTmpRight );
1848     rInf.First( nTmpFirst );
1849 
1850     rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) );
1851     rInf.Width( rInf.RealWidth() );
1852     if( ((SwTxtFormatter*)this)->GetRedln() )
1853     {
1854         ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1855         ((SwTxtFormatter*)this)->GetRedln()->Reset();
1856     }
1857 }
1858 
1859 /*************************************************************************
1860  *                      SwTxtFormatter::FormatReset()
1861  *************************************************************************/
1862 
1863 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1864 {
1865     pCurr->Truncate();
1866     pCurr->Init();
1867     if( pBlink && pCurr->IsBlinking() )
1868         pBlink->Delete( pCurr );
1869 
1870     // delete pSpaceAdd und pKanaComp
1871     pCurr->FinishSpaceAdd();
1872     pCurr->FinishKanaComp();
1873     pCurr->ResetFlags();
1874     FeedInf( rInf );
1875 }
1876 
1877 /*************************************************************************
1878  *                SwTxtFormatter::CalcOnceMore()
1879  *************************************************************************/
1880 
1881 sal_Bool SwTxtFormatter::CalcOnceMore()
1882 {
1883     if( pDropFmt )
1884     {
1885         const KSHORT nOldDrop = GetDropHeight();
1886         CalcDropHeight( pDropFmt->GetLines() );
1887         bOnceMore = nOldDrop != GetDropHeight();
1888     }
1889     else
1890         bOnceMore = sal_False;
1891     return bOnceMore;
1892 }
1893 
1894 /*************************************************************************
1895  *                SwTxtFormatter::CalcBottomLine()
1896  *************************************************************************/
1897 
1898 SwTwips SwTxtFormatter::CalcBottomLine() const
1899 {
1900     SwTwips nRet = Y() + GetLineHeight();
1901     SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom();
1902     if( nMin && ++nMin > nRet )
1903     {
1904         SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1905                         - pFrm->Prt().Top();
1906         if( nRet + nDist < nMin )
1907         {
1908             sal_Bool bRepaint = HasTruncLines() &&
1909                 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1;
1910             nRet = nMin - nDist;
1911             if( bRepaint )
1912             {
1913                 ((SwRepaint*)GetInfo().GetParaPortion()
1914                     ->GetRepaint())->Bottom( nRet-1 );
1915                 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1916             }
1917         }
1918     }
1919     return nRet;
1920 }
1921 
1922 /*************************************************************************
1923  *                SwTxtFormatter::_CalcFitToContent()
1924  *
1925  * FME/OD: This routine does a limited text formatting.
1926  *************************************************************************/
1927 
1928 SwTwips SwTxtFormatter::_CalcFitToContent()
1929 {
1930     FormatReset( GetInfo() );
1931     BuildPortions( GetInfo() );
1932     pCurr->CalcLine( *this, GetInfo() );
1933     return pCurr->Width();
1934 }
1935 
1936 /*************************************************************************
1937  *                      SwTxtFormatter::AllowRepaintOpt()
1938  *
1939  * determines if the calculation of a repaint offset is allowed
1940  * otherwise each line is painted from 0 (this is a copy of the beginning
1941  * of the former SwTxtFormatter::Recycle() function
1942  *************************************************************************/
1943 sal_Bool SwTxtFormatter::AllowRepaintOpt() const
1944 {
1945     // reformat position in front of current line? Only in this case
1946     // we want to set the repaint offset
1947     sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1948                                 pCurr->GetLen();
1949 
1950     // a special case is the last line of a block adjusted paragraph:
1951     if ( bOptimizeRepaint )
1952     {
1953         switch( GetAdjust() )
1954         {
1955         case SVX_ADJUST_BLOCK:
1956         {
1957             if( IsLastBlock() || IsLastCenter() )
1958                 bOptimizeRepaint = sal_False;
1959             else
1960             {
1961                 // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1962                 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1963                 if ( bOptimizeRepaint )
1964                 {
1965                     SwLinePortion *pPos = pCurr->GetFirstPortion();
1966                     while ( pPos && !pPos->IsFlyPortion() )
1967                         pPos = pPos->GetPortion();
1968                     bOptimizeRepaint = !pPos;
1969                 }
1970             }
1971             break;
1972         }
1973         case SVX_ADJUST_CENTER:
1974         case SVX_ADJUST_RIGHT:
1975             bOptimizeRepaint = sal_False;
1976             break;
1977         default: ;
1978         }
1979     }
1980 
1981     // Schon wieder ein Sonderfall: unsichtbare SoftHyphs
1982     const xub_StrLen nReformat = GetInfo().GetReformatStart();
1983     if( bOptimizeRepaint && STRING_LEN != nReformat )
1984     {
1985         const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat );
1986         bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
1987                             || ! GetInfo().HasHint( nReformat );
1988     }
1989 
1990     return bOptimizeRepaint;
1991 }
1992 
1993 /*************************************************************************
1994  *                      SwTxtFormatter::CalcOptRepaint()
1995  *
1996  * calculates an optimal repaint offset for the current line
1997  *************************************************************************/
1998 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd,
1999                                      const SvLongs* pFlyStart )
2000 {
2001     if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() )
2002     // the reformat position is behind our new line, that means
2003     // something of our text has moved to the next line
2004         return 0;
2005 
2006     xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd );
2007 
2008     // in case we do not have any fly in our line, our repaint position
2009     // is the changed position - 1
2010     if ( ! pFlyStart && ! pCurr->IsFly() )
2011     {
2012         // this is the maximum repaint offset determined during formatting
2013         // for example: the beginning of the first right tab stop
2014         // if this value is 0, this means that we do not have an upper
2015         // limit for the repaint offset
2016         const long nFormatRepaint = GetInfo().GetPaintOfst();
2017 
2018         if ( nReformat < GetInfo().GetLineStart() + 3 )
2019             return 0;
2020 
2021         // step back two positions for smoother repaint
2022         nReformat -= 2;
2023 
2024 #ifndef QUARTZ
2025 #ifndef ENABLE_GRAPHITE
2026         // --> FME 2004-09-27 #i28795#, #i34607#, #i38388#
2027         // step back six(!) more characters for complex scripts
2028         // this is required e.g., for Khmer (thank you, Javier!)
2029         const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
2030         xub_StrLen nMaxContext = 0;
2031         if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2032             nMaxContext = 6;
2033 #else
2034         // Some Graphite fonts need context for scripts not marked as complex
2035         static const xub_StrLen nMaxContext = 10;
2036 #endif
2037 #else
2038         // some fonts like Quartz's Zapfino need more context
2039         // TODO: query FontInfo for maximum unicode context
2040         static const xub_StrLen nMaxContext = 8;
2041 #endif
2042         if( nMaxContext > 0 )
2043         {
2044             if ( nReformat > GetInfo().GetLineStart() + nMaxContext )
2045                 nReformat = nReformat - nMaxContext;
2046             else
2047                 nReformat = GetInfo().GetLineStart();
2048         }
2049         // <--
2050 
2051         // Weird situation: Our line used to end with a hole portion
2052         // and we delete some characters at the end of our line. We have
2053         // to take care for repainting the blanks which are not anymore
2054         // covered by the hole portion
2055         while ( nReformat > GetInfo().GetLineStart() &&
2056                 CH_BLANK == GetInfo().GetChar( nReformat ) )
2057             --nReformat;
2058 
2059         ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" );
2060         SwRect aRect;
2061 
2062         // Note: GetChareRect is not const. It definitely changes the
2063         // bMulti flag. We have to save and resore the old value.
2064         sal_Bool bOldMulti = GetInfo().IsMulti();
2065         GetCharRect( &aRect, nReformat );
2066         GetInfo().SetMulti( bOldMulti );
2067 
2068         return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) :
2069                                 aRect.Left();
2070     }
2071     else
2072     {
2073         // nReformat may be wrong, if something around flys has changed:
2074         // we compare the former and the new fly positions in this line
2075         // if anything has changed, we carefully have to adjust the right
2076         // repaint position
2077         long nPOfst = 0;
2078         sal_uInt16 nCnt = 0;
2079         sal_uInt16 nX = 0;
2080         sal_uInt16 nIdx = GetInfo().GetLineStart();
2081         SwLinePortion* pPor = pCurr->GetFirstPortion();
2082 
2083         while ( pPor )
2084         {
2085             if ( pPor->IsFlyPortion() )
2086             {
2087                 // compare start of fly with former start of fly
2088                 if ( pFlyStart &&
2089                      nCnt < pFlyStart->Count() &&
2090                      nX == (*pFlyStart)[ nCnt ] &&
2091                      nIdx < nReformat
2092                    )
2093                     // found fix position, nothing has changed left from nX
2094                     nPOfst = nX + pPor->Width();
2095                 else
2096                     break;
2097 
2098                 nCnt++;
2099             }
2100             nX = nX + pPor->Width();
2101             nIdx = nIdx + pPor->GetLen();
2102             pPor = pPor->GetPortion();
2103         }
2104 
2105         return nPOfst + GetLeftMargin();
2106     }
2107 }
2108 
2109 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos )
2110 {
2111     // Only if hidden text should not be shown:
2112 //    if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2113     const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar();
2114     const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( sal_True ) && rInf.GetOpt().IsPrinting();
2115     if (bShowInDocView || bShowForPrinting)
2116         return false;
2117 
2118     const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2119     xub_StrLen nHiddenStart;
2120     xub_StrLen nHiddenEnd;
2121     rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2122     if ( nHiddenEnd )
2123     {
2124         rPos = nHiddenEnd;
2125         return true;
2126     }
2127 
2128     return false;
2129 }
2130