xref: /trunk/main/sw/source/core/text/itrform2.cxx (revision 870262e3)
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 
ClearFly(SwTxtFormatInfo & rInf)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 
CtorInitTxtFormatter(SwTxtFrm * pNewFrm,SwTxtFormatInfo * pNewInf)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( sal_False, "+SwTxtFormatter::CTOR: bad offset" );
109 		nStart = GetInfo().GetTxt().Len();
110 	}
111 
112 }
113 
114 /*************************************************************************
115  *						SwTxtFormatter::DTOR
116  *************************************************************************/
117 
~SwTxtFormatter()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 
Insert(SwLineLayout * pLay)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 
GetFrmRstHeight() const149 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 
UnderFlow(SwTxtFormatInfo & rInf)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: Losing 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 
InsertPortion(SwTxtFormatInfo & rInf,SwLinePortion * pPor) const327 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 
BuildPortions(SwTxtFormatInfo & rInf)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 
CalcAdjustLine(SwLineLayout * pCurrent)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 
CalcAscent(SwTxtFormatInfo & rInf,SwLinePortion * pPor)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:
SwMetaPortion()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 
Paint(const SwTxtPaintInfo & rInf) const850 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 
WhichTxtPor(SwTxtFormatInfo & rInf) const864 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const
865 {
866     SwTxtPortion *pPor = 0;
867     if( GetFnt()->IsTox() )
868     {
869         pPor = new SwToxPortion;
870     }
871     else if ( GetFnt()->IsInputField() )
872     {
873         pPor = new SwTxtInputFldPortion();
874     }
875     else
876     {
877         if( GetFnt()->IsRef() )
878             pPor = new SwRefPortion;
879         else if (GetFnt()->IsMeta())
880         {
881             pPor = new SwMetaPortion;
882         }
883         else
884         {
885             // Erst zum Schluss !
886             // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben,
887              // z.B. bei nicht darstellbaren Zeichen.
888             if( rInf.GetLen() > 0 )
889             {
890                 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART )
891                     pPor = new SwFieldMarkPortion();
892                 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND )
893                     pPor = new SwFieldMarkPortion();
894                 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT )
895                     pPor = new SwFieldFormPortion();
896             }
897             if( !pPor )
898             {
899                 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() )
900                     pPor = pCurr;
901                 else
902                 {
903                     pPor = new SwTxtPortion;
904                     if ( GetFnt()->IsURL() )
905                     {
906                         pPor->SetWhichPor( POR_URL );
907                     }
908                 }
909             }
910 		}
911 	}
912 	return pPor;
913 }
914 
915 /*************************************************************************
916  *						SwTxtFormatter::NewTxtPortion()
917  *************************************************************************/
918 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert:
919 // 1) Tabs
920 // 2) Linebreaks
921 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
922 // 4) naechster Attributwechsel
923 
NewTxtPortion(SwTxtFormatInfo & rInf)924 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf )
925 {
926 	// Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr
927 	// Wenn pCurr nicht von SwTxtPortion abgeleitet ist,
928 	// muessen wir duplizieren ...
929 	Seek( rInf.GetIdx() );
930 	SwTxtPortion *pPor = WhichTxtPor( rInf );
931 
932 	// until next attribute change:
933     const xub_StrLen nNextAttr = GetNextAttr();
934 	xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() );
935 
936 	// end of script type:
937 	const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() );
938 	nNextChg = Min( nNextChg, nNextScript );
939 
940 	// end of direction:
941     const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() );
942 	nNextChg = Min( nNextChg, nNextDir );
943 
944     // 7515, 7516, 3470, 6441 : Turbo-Boost
945 	// Es wird unterstellt, dass die Buchstaben eines Fonts nicht
946 	// groesser als doppelt so breit wie hoch sind.
947 	// 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen.
948 	// Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe
949 	// ergibt sich erst im CalcAscent!
950 	// 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times
951 	// New Roman besitzt einen Ascent von 182, eine Hoehe von 200
952 	// und eine Breite von 53! Daraus folgt, dass eine Zeile mit
953 	// vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von
954 	// Faktor 2 auf 8 (wg. negativen Kernings).
955 
956 	pPor->SetLen(1);
957 	CalcAscent( rInf, pPor );
958 
959     const SwFont* pTmpFnt = rInf.GetFont();
960     KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ),
961 						  KSHORT( pPor->GetAscent() ) ) / 8;
962 	if ( !nExpect )
963 		nExpect = 1;
964     nExpect = (sal_uInt16)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect));
965 	if( nExpect > rInf.GetIdx() && nNextChg > nExpect )
966 		nNextChg = Min( nExpect, rInf.GetTxt().Len() );
967 
968 	// we keep an invariant during method calls:
969 	// there are no portion ending characters like hard spaces
970 	// or tabs in [ nLeftScanIdx, nRightScanIdx ]
971     if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx )
972 	{
973 		if ( nNextChg > nRightScanIdx )
974             nNextChg = nRightScanIdx =
975                 rInf.ScanPortionEnd( nRightScanIdx, nNextChg );
976 	}
977 	else
978 	{
979 		nLeftScanIdx = rInf.GetIdx();
980         nNextChg = nRightScanIdx =
981                 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
982 	}
983 
984 	pPor->SetLen( nNextChg - rInf.GetIdx() );
985 	rInf.SetLen( pPor->GetLen() );
986 	return pPor;
987 }
988 
989 
990 /*************************************************************************
991  *				   SwTxtFormatter::WhichFirstPortion()
992  *************************************************************************/
993 
WhichFirstPortion(SwTxtFormatInfo & rInf)994 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf)
995 {
996 	SwLinePortion *pPor = 0;
997 
998 	if( rInf.GetRest() )
999 	{
1000 		// 5010: Tabs und Felder
1001 		if( '\0' != rInf.GetHookChar() )
1002 			return 0;
1003 
1004 		pPor = rInf.GetRest();
1005 		if( pPor->IsErgoSumPortion() )
1006 			rInf.SetErgoDone(sal_True);
1007 		else
1008 			if( pPor->IsFtnNumPortion() )
1009 				rInf.SetFtnDone(sal_True);
1010 			else
1011 				if( pPor->InNumberGrp() )
1012 					rInf.SetNumDone(sal_True);
1013 		if( pPor )
1014 		{
1015 			rInf.SetRest(0);
1016 			pCurr->SetRest( sal_True );
1017 			return pPor;
1018 		}
1019 	}
1020 
1021 	// ???? und ????: im Follow duerfen wir schon stehen,
1022 	// entscheidend ist, ob pFrm->GetOfst() == 0 ist!
1023 	if( rInf.GetIdx() )
1024 	{
1025 		// Nun koennen auch FtnPortions und ErgoSumPortions
1026 		// verlaengert werden.
1027 
1028 		// 1) Die ErgoSumTexte
1029 		if( !rInf.IsErgoDone() )
1030 		{
1031 			if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1032 				pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1033 			rInf.SetErgoDone( sal_True );
1034 		}
1035 
1036         // 2) Arrow portions
1037 		if( !pPor && !rInf.IsArrowDone() )
1038 		{
1039 			if( pFrm->GetOfst() && !pFrm->IsFollow() &&
1040 				rInf.GetIdx() == pFrm->GetOfst() )
1041 				pPor = new SwArrowPortion( *pCurr );
1042 			rInf.SetArrowDone( sal_True );
1043 		}
1044 
1045         // 3) Kerning portions at beginning of line in grid mode
1046         if ( ! pPor && ! pCurr->GetPortion() )
1047         {
1048             GETGRID( GetTxtFrm()->FindPageFrm() )
1049             if ( pGrid )
1050                 pPor = new SwKernPortion( *pCurr );
1051         }
1052 
1053 		// 4) Die Zeilenreste (mehrzeilige Felder)
1054 		if( !pPor )
1055 		{
1056 			pPor = rInf.GetRest();
1057 			// 6922: Nur bei pPor natuerlich.
1058 			if( pPor )
1059 			{
1060 				pCurr->SetRest( sal_True );
1061 				rInf.SetRest(0);
1062 			}
1063 		}
1064 	}
1065     else
1066 	{
1067 		// 5) Die Fussnotenzahlen
1068         if( !rInf.IsFtnDone() )
1069 		{
1070             ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1071                      "Rotated number portion trouble" )
1072 
1073             sal_Bool bFtnNum = pFrm->IsFtnNumFrm();
1074 			rInf.GetParaPortion()->SetFtnNum( bFtnNum );
1075 			if( bFtnNum )
1076 				pPor = (SwLinePortion*)NewFtnNumPortion( rInf );
1077             rInf.SetFtnDone( sal_True );
1078 		}
1079 
1080 		// 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster,
1081 		// entscheidend ist, ob der SwFtnFrm ein Follow ist.
1082         if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
1083 		{
1084 			if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
1085 				pPor = (SwLinePortion*)NewErgoSumPortion( rInf );
1086 			rInf.SetErgoDone( sal_True );
1087 		}
1088 
1089 		// 7) Die Numerierungen
1090 		if( !rInf.IsNumDone() && !pPor )
1091 		{
1092             ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(),
1093                      "Rotated number portion trouble" )
1094 
1095 			// Wenn wir im Follow stehen, dann natuerlich nicht.
1096 			if( GetTxtFrm()->GetTxtNode()->GetNumRule() )
1097                 pPor = (SwLinePortion*)NewNumberPortion( rInf );
1098 			rInf.SetNumDone( sal_True );
1099 		}
1100         // 8) Die DropCaps
1101         if( !pPor && GetDropFmt() && ! rInf.IsMulti() )
1102 			pPor = (SwLinePortion*)NewDropPortion( rInf );
1103 
1104         // 9) Kerning portions at beginning of line in grid mode
1105         if ( !pPor && !pCurr->GetPortion() )
1106         {
1107             GETGRID( GetTxtFrm()->FindPageFrm() )
1108             if ( pGrid )
1109                 pPor = new SwKernPortion( *pCurr );
1110         }
1111     }
1112 
1113         // 10) Decimal tab portion at the beginning of each line in table cells
1114         if ( !pPor && !pCurr->GetPortion() &&
1115              GetTxtFrm()->IsInTab() &&
1116              GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) )
1117         {
1118             pPor = NewTabPortion( rInf, true );
1119         }
1120 
1121         // 11) suffix of meta-field
1122         if (!pPor)
1123         {
1124             pPor = TryNewNoLengthPortion(rInf);
1125         }
1126 
1127 	return pPor;
1128 }
1129 
lcl_OldFieldRest(const SwLineLayout * pCurr)1130 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr )
1131 {
1132 	if( !pCurr->GetNext() )
1133 		return sal_False;
1134 	const SwLinePortion *pPor = pCurr->GetNext()->GetPortion();
1135 	sal_Bool bRet = sal_False;
1136 	while( pPor && !bRet )
1137 	{
1138 		bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) ||
1139 			(pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld());
1140 		if( !pPor->GetLen() )
1141 			break;
1142 		pPor = pPor->GetPortion();
1143 	}
1144 	return bRet;
1145 }
1146 
1147 /*************************************************************************
1148  *						SwTxtFormatter::NewPortion()
1149  *************************************************************************/
1150 
1151 /* NewPortion stellt rInf.nLen ein.
1152  * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr,
1153  * attrwechsel.
1154  * Drei Faelle koennen eintreten:
1155  * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert
1156  *	  -> return 0;
1157  * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert
1158  *	  -> Breite neu einstellen und return new FlyPortion
1159  * 3) Es muss eine neue Portion gebaut werden.
1160  *	  -> CalcFlyWidth emuliert ggf. die Breite und return Portion
1161  */
1162 
NewPortion(SwTxtFormatInfo & rInf)1163 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf )
1164 {
1165 	// Underflow hat Vorrang
1166 	rInf.SetStopUnderFlow( sal_False );
1167 	if( rInf.GetUnderFlow() )
1168 	{
1169 		ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" );
1170 		return UnderFlow( rInf );
1171 	}
1172 
1173 	// Wenn die Zeile voll ist, koennten noch Flys oder
1174 	// UnderFlow-LinePortions warten ...
1175 	if( rInf.IsFull() )
1176 	{
1177 		// ????: LineBreaks und Flys (bug05.sdw)
1178 		// 8450: IsDummy()
1179 		if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) )
1180 			return 0;
1181 
1182 		// Wenn der Text an den Fly gestossen ist, oder wenn
1183 		// der Fly als erstes drankommt, weil er ueber dem linken
1184 		// Rand haengt, wird GetFly() returnt.
1185 		// Wenn IsFull() und kein GetFly() vorhanden ist, gibt's
1186 		// naturgemaesz eine 0.
1187 		if( rInf.GetFly() )
1188 		{
1189             if( rInf.GetLast()->IsBreakPortion() )
1190             {
1191                 delete rInf.GetFly();
1192                 rInf.SetFly( 0 );
1193             }
1194 
1195             return rInf.GetFly();
1196 		}
1197 		// Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den
1198 		// Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest
1199 		// bekanntgeben, damit SwTxtFrm::Format nicht abbricht
1200 		// (die Textmasse wurde ja durchformatiert).
1201 		if( rInf.GetRest() )
1202 			rInf.SetNewLine( sal_True );
1203 		else
1204 		{
1205 			// Wenn die naechste Zeile mit einem Rest eines Feldes beginnt,
1206 			// jetzt aber kein Rest mehr anliegt,
1207 			// muss sie auf jeden Fall neu formatiert werden!
1208 			if( lcl_OldFieldRest( GetCurr() ) )
1209 				rInf.SetNewLine( sal_True );
1210 			else
1211 			{
1212 				SwLinePortion *pFirst = WhichFirstPortion( rInf );
1213 				if( pFirst )
1214 				{
1215 					rInf.SetNewLine( sal_True );
1216 					if( pFirst->InNumberGrp() )
1217 						rInf.SetNumDone( sal_False) ;
1218 					delete pFirst;
1219 				}
1220 			}
1221 		}
1222 
1223 		return 0;
1224 	}
1225 
1226 	SwLinePortion *pPor = WhichFirstPortion( rInf );
1227 
1228     // Check for Hidden Portion:
1229     if ( !pPor )
1230     {
1231     	xub_StrLen nEnd = rInf.GetIdx();
1232         if ( lcl_BuildHiddenPortion( rInf, nEnd ) )
1233             pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
1234     }
1235 
1236 	if( !pPor )
1237 	{
1238         if( ( !pMulti || pMulti->IsBidi() ) &&
1239             // --> FME 2005-02-14 #i42734#
1240             // No multi portion if there is a hook character waiting:
1241             ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
1242             // <--
1243         {
1244             // We open a multiportion part, if we enter a multi-line part
1245 			// of the paragraph.
1246         	xub_StrLen nEnd = rInf.GetIdx();
1247             SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti );
1248 			if( pCreate )
1249 			{
1250 				SwMultiPortion* pTmp = NULL;
1251 
1252                 if ( SW_MC_BIDI == pCreate->nId )
1253                     pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
1254                 else if ( SW_MC_RUBY == pCreate->nId )
1255                 {
1256                     Seek( rInf.GetIdx() );
1257                     sal_Bool bRubyTop;
1258                     sal_Bool* pRubyPos = 0;
1259 
1260                     if ( rInf.SnapToGrid() )
1261                     {
1262                         GETGRID( GetTxtFrm()->FindPageFrm() )
1263                         if ( pGrid )
1264                         {
1265                             bRubyTop = ! pGrid->GetRubyTextBelow();
1266                             pRubyPos = &bRubyTop;
1267                         }
1268                     }
1269 
1270                     pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
1271                                               *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(),
1272                                               nEnd, 0, pRubyPos );
1273                 }
1274 				else if( SW_MC_ROTATE == pCreate->nId )
1275                     pTmp = new SwRotatedPortion( *pCreate, nEnd,
1276                                                  GetTxtFrm()->IsRightToLeft() );
1277 				else
1278 					pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
1279 
1280                 delete pCreate;
1281                 CalcFlyWidth( rInf );
1282 
1283 				return pTmp;
1284 			}
1285 		}
1286 		// 5010: Tabs und Felder
1287 		xub_Unicode cChar = rInf.GetHookChar();
1288 
1289 		if( cChar )
1290 		{
1291 			/* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das
1292 			 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile
1293 			 * gewandert ist ( so geschehen hinter Rahmen ).
1294 			 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir
1295 			 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei
1296 			 * DezimalTabs und Feldern (22615)
1297 			*/
1298 			if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() )
1299 				cChar = rInf.GetChar( rInf.GetIdx() );
1300 		    rInf.ClearHookChar();
1301 		}
1302 		else
1303 		{
1304 			if( rInf.GetIdx() >= rInf.GetTxt().Len() )
1305 			{
1306 				rInf.SetFull(sal_True);
1307                 CalcFlyWidth( rInf );
1308 				return pPor;
1309 			}
1310 			cChar = rInf.GetChar( rInf.GetIdx() );
1311 		}
1312 
1313         switch( cChar )
1314         {
1315             case CH_TAB:
1316                 pPor = NewTabPortion( rInf, false ); break;
1317 
1318             case CH_BREAK:
1319                 pPor = new SwBreakPortion( *rInf.GetLast() ); break;
1320 
1321             case CHAR_SOFTHYPHEN:					// soft hyphen
1322                 pPor = new SwSoftHyphPortion; break;
1323 
1324             case CHAR_HARDBLANK:					// no-break space
1325                 pPor = new SwBlankPortion( ' ' ); break;
1326 
1327             case CHAR_HARDHYPHEN:               // non-breaking hyphen
1328                 pPor = new SwBlankPortion( '-' ); break;
1329 
1330             case CHAR_ZWSP:                     // zero width space
1331             case CHAR_ZWNBSP :                  // word joiner
1332                 pPor = new SwControlCharPortion( cChar ); break;
1333 
1334             case CH_TXTATR_BREAKWORD:
1335             case CH_TXTATR_INWORD:
1336                 if( rInf.HasHint( rInf.GetIdx() ) )
1337                 {
1338                     pPor = NewExtraPortion( rInf );
1339                     break;
1340                 }
1341                 // No break
1342             default 	   :
1343                 {
1344                 SwTabPortion* pLastTabPortion = rInf.GetLastTab();
1345                 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
1346                 {
1347                     // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full:
1348                     // We have a decimal tab portion in the line and the next character has to be
1349                     // aligned at the tab stop position. We store the width from the beginning of
1350                     // the tab stop portion up to the portion containint the decimal separator:
1351 				  if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
1352 						 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() )
1353                     {
1354                         ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" )
1355                         const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() );
1356                         static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
1357                         rInf.SetTabDecimal( 0 );
1358                     }
1359                     // <--
1360                     else
1361                         rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
1362                 }
1363 
1364 				if( rInf.GetRest() )
1365 				{
1366 					if( rInf.IsFull() )
1367 					{
1368 						rInf.SetNewLine(sal_True);
1369 						return 0;
1370 					}
1371 					pPor = rInf.GetRest();
1372 					rInf.SetRest(0);
1373 				}
1374 				else
1375 				{
1376 					if( rInf.IsFull() )
1377 						return 0;
1378 					pPor = NewTxtPortion( rInf );
1379 				}
1380 				break;
1381 			}
1382 		}
1383 
1384 		// Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht,
1385 		// dann haben wir es mit einem Feld zu tun, das sich aufgesplittet
1386 		// hat, weil z.B. ein Tab enthalten ist.
1387 		if( pPor && rInf.GetRest() )
1388 			pPor->SetLen( 0 );
1389 
1390 		// robust:
1391 		if( !pPor || rInf.IsStop() )
1392 		{
1393 			delete pPor;
1394 			return 0;
1395 		}
1396 	}
1397 
1398     // Special portions containing numbers (footnote anchor, footnote number,
1399     // numbering) can be contained in a rotated portion, if the user
1400     // choose a rotated character attribute.
1401     if ( pPor && ! pMulti )
1402     {
1403         if ( pPor->IsFtnPortion() )
1404         {
1405             const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn();
1406 
1407             if ( pTxtFtn )
1408             {
1409                 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1410                 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1411                 const SwEndNoteInfo* pInfo;
1412                 if( rFtn.IsEndNote() )
1413                     pInfo = &pDoc->GetEndNoteInfo();
1414                 else
1415                     pInfo = &pDoc->GetFtnInfo();
1416                 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1417 
1418                 const SfxPoolItem* pItem;
1419                 sal_uInt16 nDir = 0;
1420                 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1421                     sal_True, &pItem ))
1422                     nDir = ((SvxCharRotateItem*)pItem)->GetValue();
1423 
1424                 if ( 0 != nDir )
1425                 {
1426                     delete pPor;
1427                     pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ?
1428                                                     DIR_BOTTOM2TOP :
1429                                                     DIR_TOP2BOTTOM );
1430                 }
1431             }
1432         }
1433         else if ( pPor->InNumberGrp() )
1434         {
1435             const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont();
1436 
1437             if ( pNumFnt )
1438             {
1439                 sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
1440                 if ( 0 != nDir )
1441                 {
1442                     delete pPor;
1443                     pPor = new SwRotatedPortion( 0, 900 == nDir ?
1444                                                     DIR_BOTTOM2TOP :
1445                                                     DIR_TOP2BOTTOM );
1446 
1447                     rInf.SetNumDone( sal_False );
1448                     rInf.SetFtnDone( sal_False );
1449                 }
1450             }
1451         }
1452     }
1453 
1454     // Der Font wird im Outputdevice eingestellt,
1455 	// der Ascent und die Hoehe werden berechnet.
1456 	if( !pPor->GetAscent() && !pPor->Height() )
1457 		CalcAscent( rInf, pPor );
1458 	rInf.SetLen( pPor->GetLen() );
1459 
1460 	// In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt.
1461     CalcFlyWidth( rInf );
1462 
1463 	// Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige
1464 	// Werte bereithalten muss:
1465 	if( !pCurr->Height() )
1466 	{
1467 		ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" );
1468 		pCurr->Height( pPor->Height() );
1469 		pCurr->SetAscent( pPor->GetAscent() );
1470 	}
1471 
1472 	ASSERT( !pPor || pPor->Height(),
1473 			"SwTxtFormatter::NewPortion: something went wrong");
1474 	if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
1475 	{
1476 		delete pPor;
1477 		pPor = rInf.GetFly();
1478 	}
1479 	return pPor;
1480 }
1481 
1482 /*************************************************************************
1483  *						SwTxtFormatter::FormatLine()
1484  *************************************************************************/
1485 
FormatLine(const xub_StrLen nStartPos)1486 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos )
1487 {
1488     ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
1489             "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" );
1490 
1491     // For the formatting routines, we set pOut to the reference device.
1492     SwHookOut aHook( GetInfo() );
1493 	if( GetInfo().GetLen() < GetInfo().GetTxt().Len() )
1494 		GetInfo().SetLen( GetInfo().GetTxt().Len() );
1495 
1496 	sal_Bool bBuild = sal_True;
1497 	SetFlyInCntBase( sal_False );
1498 	GetInfo().SetLineHeight( 0 );
1499     GetInfo().SetLineNettoHeight( 0 );
1500 
1501 	// Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden
1502 	// und auch bei geaendertem Ascent (Absenken der Grundlinie).
1503 	const KSHORT nOldHeight = pCurr->Height();
1504 	const KSHORT nOldAscent = pCurr->GetAscent();
1505 
1506 	pCurr->SetEndHyph( sal_False );
1507 	pCurr->SetMidHyph( sal_False );
1508 
1509     // fly positioning can make it necessary format a line several times
1510     // for this, we have to keep a copy of our rest portion
1511     SwLinePortion* pFld = GetInfo().GetRest();
1512     SwFldPortion* pSaveFld = 0;
1513 
1514     if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() )
1515         pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) );
1516 
1517     // for an optimal repaint rectangle, we want to compare fly portions
1518     // before and after the BuildPortions call
1519     const sal_Bool bOptimizeRepaint = AllowRepaintOpt();
1520     const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen();
1521     SvLongs* pFlyStart = 0;
1522 
1523     // these are the conditions for a fly position comparison
1524     if ( bOptimizeRepaint && pCurr->IsFly() )
1525     {
1526         pFlyStart = new SvLongs;
1527         SwLinePortion* pPor = pCurr->GetFirstPortion();
1528         long nPOfst = 0;
1529         sal_uInt16 nCnt = 0;
1530 
1531         while ( pPor )
1532         {
1533             if ( pPor->IsFlyPortion() )
1534                 // insert start value of fly portion
1535                 pFlyStart->Insert( nPOfst, nCnt++ );
1536 
1537             nPOfst += pPor->Width();
1538             pPor = pPor->GetPortion();
1539         }
1540     }
1541 
1542     // Hier folgt bald die Unterlaufpruefung.
1543 	while( bBuild )
1544 	{
1545 		GetInfo().SetFtnInside( sal_False );
1546         GetInfo().SetOtherThanFtnInside( sal_False );
1547 
1548 		// These values must not be reset by FormatReset();
1549 		sal_Bool bOldNumDone = GetInfo().IsNumDone();
1550 		sal_Bool bOldArrowDone = GetInfo().IsArrowDone();
1551 		sal_Bool bOldErgoDone = GetInfo().IsErgoDone();
1552 
1553         // besides other things, this sets the repaint offset to 0
1554         FormatReset( GetInfo() );
1555 
1556 		GetInfo().SetNumDone( bOldNumDone );
1557 		GetInfo().SetArrowDone( bOldArrowDone );
1558 		GetInfo().SetErgoDone( bOldErgoDone );
1559 
1560         // build new portions for this line
1561         BuildPortions( GetInfo() );
1562 
1563         if( GetInfo().IsStop() )
1564 		{
1565 			pCurr->SetLen( 0 );
1566 			pCurr->Height( GetFrmRstHeight() + 1 );
1567 			pCurr->SetRealHeight( GetFrmRstHeight() + 1 );
1568 			pCurr->Width(0);
1569 			pCurr->Truncate();
1570             return nStartPos;
1571 		}
1572 		else if( GetInfo().IsDropInit() )
1573 		{
1574 			DropInit();
1575 			GetInfo().SetDropInit( sal_False );
1576 		}
1577 
1578         pCurr->CalcLine( *this, GetInfo() );
1579 		CalcRealHeight( GetInfo().IsNewLine() );
1580 
1581 		//Bug 120864:For Special case that at the first caculation couldn't get correct height. And need to recaculate for the right height.
1582 		SwLinePortion* pPorTmp = pCurr->GetPortion();
1583 		if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetPortion() &&
1584 			pCurr->Height() > pPorTmp->Height())))
1585 		//Bug 120864(End)
1586 		{
1587 			KSHORT nTmpAscent, nTmpHeight;
1588 			CalcAscentAndHeight( nTmpAscent, nTmpHeight );
1589 			AlignFlyInCntBase( Y() + long( nTmpAscent ) );
1590 			pCurr->CalcLine( *this, GetInfo() );
1591 			CalcRealHeight();
1592 		}
1593 
1594 		// bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird
1595         if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
1596         {
1597             pCurr->SetRealHeight( GetInfo().GetLineHeight() );
1598             bBuild = sal_False;
1599         }
1600         else
1601         {
1602             bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow(GetInfo()) )
1603                      || GetInfo().CheckFtnPortion(pCurr);
1604             if( bBuild )
1605             {
1606                 GetInfo().SetNumDone( bOldNumDone );
1607                 GetInfo().ResetMaxWidthDiff();
1608 
1609                 // delete old rest
1610                 if ( GetInfo().GetRest() )
1611                 {
1612                     delete GetInfo().GetRest();
1613                     GetInfo().SetRest( 0 );
1614                 }
1615 
1616                 // set original rest portion
1617                 if ( pSaveFld )
1618                     GetInfo().SetRest( new SwFldPortion( *pSaveFld ) );
1619 
1620                 pCurr->SetLen( 0 );
1621                 pCurr->Width(0);
1622                 pCurr->Truncate();
1623             }
1624         }
1625 	}
1626 
1627     // calculate optimal repaint rectangle
1628     if ( bOptimizeRepaint )
1629     {
1630         GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) );
1631         if ( pFlyStart )
1632             delete pFlyStart;
1633     }
1634     else
1635         // Special case: We do not allow an optimitation of the repaint
1636         // area, but during formatting the repaint offset is set to indicate
1637         // a maximum value for the offset. This value has to be reset:
1638         GetInfo().SetPaintOfst( 0 );
1639 
1640     // This corrects the start of the reformat range if something has
1641     // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
1642     // will give us a wrong result if we have to reformat another line
1643     GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() );
1644 
1645     // delete master copy of rest portion
1646     if ( pSaveFld )
1647         delete pSaveFld;
1648 
1649     xub_StrLen nNewStart = nStartPos + pCurr->GetLen();
1650 
1651     // adjust text if kana compression is enabled
1652     if ( GetInfo().CompressLine() )
1653     {
1654         SwTwips nRepaintOfst = CalcKanaAdj( pCurr );
1655 
1656         // adjust repaint offset
1657         if ( nRepaintOfst < GetInfo().GetPaintOfst() )
1658             GetInfo().SetPaintOfst( nRepaintOfst );
1659     }
1660 
1661     CalcAdjustLine( pCurr );
1662 
1663 	if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() )
1664 	{
1665 		SetFlyInCntBase();
1666 		GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling
1667 		// alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind
1668 		// auch formatiert werden.
1669 		GetInfo().SetShift( sal_True );
1670 	}
1671 
1672 	if ( IsFlyInCntBase() && !IsQuick() )
1673 		UpdatePos( pCurr, GetTopLeft(), GetStart() );
1674 
1675 	return nNewStart;
1676 }
1677 
1678 /*************************************************************************
1679  *                      SwTxtFormatter::RecalcRealHeight()
1680  *************************************************************************/
1681 
RecalcRealHeight()1682 void SwTxtFormatter::RecalcRealHeight()
1683 {
1684 	sal_Bool bMore = sal_True;
1685 	while(bMore)
1686 	{
1687 		DBG_LOOP;
1688 		CalcRealHeight();
1689 		bMore = Next() != 0;
1690 	}
1691 }
1692 
1693 /*************************************************************************
1694  *                    SwTxtFormatter::CalcRealHeight()
1695  *************************************************************************/
1696 
CalcRealHeight(sal_Bool bNewLine)1697 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine )
1698 {
1699 	KSHORT nLineHeight = pCurr->Height();
1700 	pCurr->SetClipping( sal_False );
1701 
1702     GETGRID( pFrm->FindPageFrm() )
1703     if ( pGrid && GetInfo().SnapToGrid() )
1704     {
1705         const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
1706         const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
1707         const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow();
1708 
1709         nLineHeight = nGridWidth + nRubyHeight;
1710         sal_uInt16 nLineDist = nLineHeight;
1711 
1712         while ( pCurr->Height() > nLineHeight )
1713             nLineHeight = nLineHeight + nLineDist;
1714 
1715         KSHORT nAsc = pCurr->GetAscent() +
1716                       ( bRubyTop ?
1717                        ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 :
1718                        ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 );
1719 
1720         pCurr->Height( nLineHeight );
1721         pCurr->SetAscent( nAsc );
1722         pInf->GetParaPortion()->SetFixLineHeight();
1723 
1724         // we ignore any line spacing options except from ...
1725         const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing();
1726         if ( ! IsParaLine() && pSpace &&
1727              SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() )
1728         {
1729             sal_uLong nTmp = pSpace->GetPropLineSpace();
1730 
1731             if( nTmp < 100 )
1732                 nTmp = 100;
1733 
1734             nTmp *= nLineHeight;
1735             nLineHeight = (sal_uInt16)(nTmp / 100);
1736         }
1737 
1738         pCurr->SetRealHeight( nLineHeight );
1739         return;
1740     }
1741 
1742 	// Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese
1743 	// sollten kein Register etc. beachten. Dummerweise hat kann es eine leere
1744 	// Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem
1745 	// Shift-Return), die das Register durchaus beachten soll.
1746     if( !pCurr->IsDummy() || ( !pCurr->GetNext() &&
1747         GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) )
1748     {
1749         const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
1750         if( pSpace )
1751         {
1752             switch( pSpace->GetLineSpaceRule() )
1753             {
1754                 case SVX_LINE_SPACE_AUTO:
1755                 break;
1756                 case SVX_LINE_SPACE_MIN:
1757                 {
1758                     if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) )
1759                         nLineHeight = pSpace->GetLineHeight();
1760                     break;
1761                 }
1762                 case SVX_LINE_SPACE_FIX:
1763                 {
1764                     nLineHeight = pSpace->GetLineHeight();
1765                     KSHORT nAsc = ( 4 * nLineHeight ) / 5;  // 80%
1766                     if( nAsc < pCurr->GetAscent() ||
1767                         nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() )
1768                         pCurr->SetClipping( sal_True );
1769                     pCurr->Height( nLineHeight );
1770                     pCurr->SetAscent( nAsc );
1771                     pInf->GetParaPortion()->SetFixLineHeight();
1772                 }
1773                 break;
1774                 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
1775             }
1776             if( !IsParaLine() )
1777                 switch( pSpace->GetInterLineSpaceRule() )
1778                 {
1779                     case SVX_INTER_LINE_SPACE_OFF:
1780                     break;
1781                     case SVX_INTER_LINE_SPACE_PROP:
1782                     {
1783                         long nTmp = pSpace->GetPropLineSpace();
1784                         // 50% ist das Minimum, bei 0% schalten wir auf
1785                         // den Defaultwert 100% um ...
1786                         if( nTmp < 50 )
1787                             nTmp = nTmp ? 50 : 100;
1788 
1789                         nTmp *= nLineHeight;
1790                         nTmp /= 100;
1791                         if( !nTmp )
1792                             ++nTmp;
1793                         nLineHeight = (KSHORT)nTmp;
1794                         break;
1795                     }
1796                     case SVX_INTER_LINE_SPACE_FIX:
1797                     {
1798                         nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
1799                         break;
1800                     }
1801                     default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
1802                 }
1803         }
1804 #if OSL_DEBUG_LEVEL > 1
1805         KSHORT nDummy = nLineHeight + 1;
1806         (void)nDummy;
1807 #endif
1808 
1809         if( IsRegisterOn() )
1810         {
1811             SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height();
1812             SWRECTFN( pFrm )
1813             if ( bVert )
1814                 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY );
1815             nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() );
1816             KSHORT nDiff = KSHORT( nTmpY % RegDiff() );
1817             if( nDiff )
1818                 nLineHeight += RegDiff() - nDiff;
1819         }
1820     }
1821 	pCurr->SetRealHeight( nLineHeight );
1822 }
1823 
1824 /*************************************************************************
1825  *						SwTxtFormatter::FeedInf()
1826  *************************************************************************/
1827 
FeedInf(SwTxtFormatInfo & rInf) const1828 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const
1829 {
1830 	// 3260, 3860: Fly auf jeden Fall loeschen!
1831 	ClearFly( rInf );
1832 	rInf.Init();
1833 
1834 	rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
1835 	rInf.SetRoot( pCurr );
1836 	rInf.SetLineStart( nStart );
1837 	rInf.SetIdx( nStart );
1838 
1839     // Handle overflows:
1840     // --> FME 2004-11-25 #i34348# Changed type from sal_uInt16 to SwTwips
1841     SwTwips nTmpLeft = Left();
1842     SwTwips nTmpRight = Right();
1843     SwTwips nTmpFirst = FirstLeft();
1844     // <--
1845 
1846     if ( nTmpLeft > USHRT_MAX ||
1847          nTmpRight > USHRT_MAX ||
1848          nTmpFirst > USHRT_MAX )
1849     {
1850         SWRECTFN( rInf.GetTxtFrm() )
1851         nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)();
1852         nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)();
1853         nTmpFirst = nTmpLeft;
1854     }
1855 
1856     rInf.Left(  nTmpLeft  );
1857     rInf.Right( nTmpRight );
1858     rInf.First( nTmpFirst );
1859 
1860 	rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) );
1861 	rInf.Width( rInf.RealWidth() );
1862 	if( ((SwTxtFormatter*)this)->GetRedln() )
1863 	{
1864 		((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() );
1865 		((SwTxtFormatter*)this)->GetRedln()->Reset();
1866 	}
1867 }
1868 
1869 /*************************************************************************
1870  *						SwTxtFormatter::FormatReset()
1871  *************************************************************************/
1872 
FormatReset(SwTxtFormatInfo & rInf)1873 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf )
1874 {
1875 	pCurr->Truncate();
1876 	pCurr->Init();
1877 	if( pBlink && pCurr->IsBlinking() )
1878 		pBlink->Delete( pCurr );
1879 
1880     // delete pSpaceAdd und pKanaComp
1881     pCurr->FinishSpaceAdd();
1882     pCurr->FinishKanaComp();
1883 	pCurr->ResetFlags();
1884 	FeedInf( rInf );
1885 }
1886 
1887 /*************************************************************************
1888  *				  SwTxtFormatter::CalcOnceMore()
1889  *************************************************************************/
1890 
CalcOnceMore()1891 sal_Bool SwTxtFormatter::CalcOnceMore()
1892 {
1893 	if( pDropFmt )
1894 	{
1895 		const KSHORT nOldDrop = GetDropHeight();
1896 		CalcDropHeight( pDropFmt->GetLines() );
1897 		bOnceMore = nOldDrop != GetDropHeight();
1898 	}
1899 	else
1900 		bOnceMore = sal_False;
1901 	return bOnceMore;
1902 }
1903 
1904 /*************************************************************************
1905  *				  SwTxtFormatter::CalcBottomLine()
1906  *************************************************************************/
1907 
CalcBottomLine() const1908 SwTwips SwTxtFormatter::CalcBottomLine() const
1909 {
1910 	SwTwips nRet = Y() + GetLineHeight();
1911 	SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom();
1912 	if( nMin && ++nMin > nRet )
1913 	{
1914 		SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height()
1915 						- pFrm->Prt().Top();
1916 		if( nRet + nDist < nMin )
1917 		{
1918 			sal_Bool bRepaint = HasTruncLines() &&
1919 				GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1;
1920 			nRet = nMin - nDist;
1921 			if( bRepaint )
1922 			{
1923 				((SwRepaint*)GetInfo().GetParaPortion()
1924 					->GetRepaint())->Bottom( nRet-1 );
1925 				((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 );
1926 			}
1927 		}
1928 	}
1929 	return nRet;
1930 }
1931 
1932 /*************************************************************************
1933  *				  SwTxtFormatter::_CalcFitToContent()
1934  *
1935  * FME/OD: This routine does a limited text formatting.
1936  *************************************************************************/
1937 
_CalcFitToContent()1938 SwTwips SwTxtFormatter::_CalcFitToContent()
1939 {
1940     FormatReset( GetInfo() );
1941     BuildPortions( GetInfo() );
1942     pCurr->CalcLine( *this, GetInfo() );
1943     return pCurr->Width();
1944 }
1945 
1946 /*************************************************************************
1947  *                      SwTxtFormatter::AllowRepaintOpt()
1948  *
1949  * determines if the calculation of a repaint offset is allowed
1950  * otherwise each line is painted from 0 (this is a copy of the beginning
1951  * of the former SwTxtFormatter::Recycle() function
1952  *************************************************************************/
AllowRepaintOpt() const1953 sal_Bool SwTxtFormatter::AllowRepaintOpt() const
1954 {
1955     // reformat position in front of current line? Only in this case
1956     // we want to set the repaint offset
1957     sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() &&
1958                                 pCurr->GetLen();
1959 
1960     // a special case is the last line of a block adjusted paragraph:
1961     if ( bOptimizeRepaint )
1962     {
1963         switch( GetAdjust() )
1964         {
1965         case SVX_ADJUST_BLOCK:
1966         {
1967             if( IsLastBlock() || IsLastCenter() )
1968                 bOptimizeRepaint = sal_False;
1969             else
1970             {
1971                 // ????: Blank in der letzten Masterzeile (blocksat.sdw)
1972                 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow();
1973                 if ( bOptimizeRepaint )
1974                 {
1975                     SwLinePortion *pPos = pCurr->GetFirstPortion();
1976                     while ( pPos && !pPos->IsFlyPortion() )
1977                         pPos = pPos->GetPortion();
1978                     bOptimizeRepaint = !pPos;
1979                 }
1980             }
1981             break;
1982         }
1983         case SVX_ADJUST_CENTER:
1984         case SVX_ADJUST_RIGHT:
1985             bOptimizeRepaint = sal_False;
1986             break;
1987         default: ;
1988         }
1989     }
1990 
1991 	// Schon wieder ein Sonderfall: unsichtbare SoftHyphs
1992     const xub_StrLen nReformat = GetInfo().GetReformatStart();
1993     if( bOptimizeRepaint && STRING_LEN != nReformat )
1994 	{
1995         const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat );
1996         bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
1997                             || ! GetInfo().HasHint( nReformat );
1998 	}
1999 
2000     return bOptimizeRepaint;
2001 }
2002 
2003 /*************************************************************************
2004  *                      SwTxtFormatter::CalcOptRepaint()
2005  *
2006  * calculates an optimal repaint offset for the current line
2007  *************************************************************************/
CalcOptRepaint(xub_StrLen nOldLineEnd,const SvLongs * pFlyStart)2008 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd,
2009                                      const SvLongs* pFlyStart )
2010 {
2011     if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() )
2012     // the reformat position is behind our new line, that means
2013     // something of our text has moved to the next line
2014         return 0;
2015 
2016     xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd );
2017 
2018     // in case we do not have any fly in our line, our repaint position
2019     // is the changed position - 1
2020     if ( ! pFlyStart && ! pCurr->IsFly() )
2021     {
2022         // this is the maximum repaint offset determined during formatting
2023         // for example: the beginning of the first right tab stop
2024         // if this value is 0, this means that we do not have an upper
2025         // limit for the repaint offset
2026         const long nFormatRepaint = GetInfo().GetPaintOfst();
2027 
2028         if ( nReformat < GetInfo().GetLineStart() + 3 )
2029             return 0;
2030 
2031         // step back two positions for smoother repaint
2032         nReformat -= 2;
2033 
2034 #ifndef QUARTZ
2035 #ifndef ENABLE_GRAPHITE
2036         // --> FME 2004-09-27 #i28795#, #i34607#, #i38388#
2037         // step back six(!) more characters for complex scripts
2038         // this is required e.g., for Khmer (thank you, Javier!)
2039         const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
2040         xub_StrLen nMaxContext = 0;
2041         if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) )
2042             nMaxContext = 6;
2043 #else
2044         // Some Graphite fonts need context for scripts not marked as complex
2045         static const xub_StrLen nMaxContext = 10;
2046 #endif
2047 #else
2048         // some fonts like Quartz's Zapfino need more context
2049         // TODO: query FontInfo for maximum unicode context
2050         static const xub_StrLen nMaxContext = 8;
2051 #endif
2052         if( nMaxContext > 0 )
2053         {
2054             if ( nReformat > GetInfo().GetLineStart() + nMaxContext )
2055                 nReformat = nReformat - nMaxContext;
2056             else
2057                 nReformat = GetInfo().GetLineStart();
2058         }
2059         // <--
2060 
2061         // Weird situation: Our line used to end with a hole portion
2062         // and we delete some characters at the end of our line. We have
2063         // to take care for repainting the blanks which are not anymore
2064         // covered by the hole portion
2065         while ( nReformat > GetInfo().GetLineStart() &&
2066                 CH_BLANK == GetInfo().GetChar( nReformat ) )
2067             --nReformat;
2068 
2069         ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" );
2070         SwRect aRect;
2071 
2072         // Note: GetChareRect is not const. It definitely changes the
2073         // bMulti flag. We have to save and resore the old value.
2074         sal_Bool bOldMulti = GetInfo().IsMulti();
2075         GetCharRect( &aRect, nReformat );
2076         GetInfo().SetMulti( bOldMulti );
2077 
2078         return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) :
2079                                 aRect.Left();
2080     }
2081     else
2082     {
2083         // nReformat may be wrong, if something around flys has changed:
2084         // we compare the former and the new fly positions in this line
2085         // if anything has changed, we carefully have to adjust the right
2086         // repaint position
2087         long nPOfst = 0;
2088         sal_uInt16 nCnt = 0;
2089         sal_uInt16 nX = 0;
2090         sal_uInt16 nIdx = GetInfo().GetLineStart();
2091         SwLinePortion* pPor = pCurr->GetFirstPortion();
2092 
2093         while ( pPor )
2094         {
2095             if ( pPor->IsFlyPortion() )
2096             {
2097                 // compare start of fly with former start of fly
2098                 if ( pFlyStart &&
2099                      nCnt < pFlyStart->Count() &&
2100                      nX == (*pFlyStart)[ nCnt ] &&
2101                      nIdx < nReformat
2102                    )
2103                     // found fix position, nothing has changed left from nX
2104                     nPOfst = nX + pPor->Width();
2105                 else
2106                     break;
2107 
2108                 nCnt++;
2109             }
2110             nX = nX + pPor->Width();
2111             nIdx = nIdx + pPor->GetLen();
2112             pPor = pPor->GetPortion();
2113         }
2114 
2115         return nPOfst + GetLeftMargin();
2116     }
2117 }
2118 
lcl_BuildHiddenPortion(const SwTxtSizeInfo & rInf,xub_StrLen & rPos)2119 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos )
2120 {
2121     // Only if hidden text should not be shown:
2122 //    if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
2123     const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar();
2124     const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( sal_True ) && rInf.GetOpt().IsPrinting();
2125     if (bShowInDocView || bShowForPrinting)
2126         return false;
2127 
2128     const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
2129     xub_StrLen nHiddenStart;
2130     xub_StrLen nHiddenEnd;
2131     rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
2132     if ( nHiddenEnd )
2133     {
2134         rPos = nHiddenEnd;
2135         return true;
2136     }
2137 
2138     return false;
2139 }
2140