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