xref: /AOO41X/main/sw/source/core/text/pormulti.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 
32 #include <hintids.hxx>
33 
34 #include <com/sun/star/i18n/ScriptType.hdl>
35 #include <editeng/twolinesitem.hxx>
36 #include <editeng/charrotateitem.hxx>
37 #include <vcl/outdev.hxx>
38 #include <fmtfld.hxx>
39 #include <fldbas.hxx>      // SwField
40 #include <txatbase.hxx>
41 #include <fmtruby.hxx> 	// SwFmtRuby
42 #include <txtatr.hxx>   // SwTxtRuby
43 #include <charfmt.hxx>
44 #include <txtinet.hxx>
45 #include <fchrfmt.hxx>
46 #include <layfrm.hxx>		// GetUpper()
47 #include <SwPortionHandler.hxx>
48 #include <pormulti.hxx> 	// SwMultiPortion
49 #include <inftxt.hxx>		// SwTxtSizeInfo
50 #include <itrpaint.hxx>     // SwTxtPainter
51 #include <viewopt.hxx>		// SwViewOptions
52 #include <itrform2.hxx>		// SwTxtFormatter
53 #include <porfld.hxx>		// SwFldPortion
54 #include <porglue.hxx>
55 #include <breakit.hxx>
56 #include <pagefrm.hxx>
57 #include <rowfrm.hxx>
58 #include <pagedesc.hxx> // SwPageDesc
59 #include <tgrditem.hxx>
60 #include <swtable.hxx>
61 #include <fmtfsize.hxx>
62 
63 using namespace ::com::sun::star;
64 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
65 
66 /*-----------------10.10.00 15:23-------------------
67  *  class SwMultiPortion
68  *
69  * A SwMultiPortion is not a simple portion,
70  * it's a container, which contains almost a SwLineLayoutPortion.
71  * This SwLineLayout could be followed by other textportions via pPortion
72  * and by another SwLineLayout via pNext to realize a doubleline portion.
73  * --------------------------------------------------*/
74 
75 SwMultiPortion::~SwMultiPortion()
76 {
77 	delete pFldRest;
78 }
79 
80 void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const
81 {
82 	ASSERT( sal_False,
83 	"Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" );
84 }
85 
86 /*-----------------13.10.00 16:21-------------------
87  * Summarize the internal lines to calculate the (external) size.
88  * The internal line has to calculate first.
89  * --------------------------------------------------*/
90 
91 void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf )
92 {
93 	Width( 0 );
94 	Height( 0 );
95 	SetAscent( 0 );
96 	SetFlyInCntnt( sal_False );
97 	SwLineLayout *pLay = &GetRoot();
98 	do
99 	{
100 		pLay->CalcLine( rLine, rInf );
101 		if( rLine.IsFlyInCntBase() )
102 			SetFlyInCntnt( sal_True );
103 		if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
104 		{
105 			// An empty phonetic line don't need an ascent or a height.
106 			if( !pLay->Width() )
107 			{
108 				pLay->SetAscent( 0 );
109 				pLay->Height( 0 );
110 			}
111 			if( OnTop() )
112 				SetAscent( GetAscent() + pLay->Height() );
113 		}
114 		else
115 			SetAscent( GetAscent() + pLay->GetAscent() );
116 		Height( Height() + pLay->Height() );
117 		if( Width() < pLay->Width() )
118 			Width( pLay->Width() );
119 		pLay = pLay->GetNext();
120 	} while ( pLay );
121 	if( HasBrackets() )
122 	{
123 		KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight;
124 		if( nTmp > Height() )
125 		{
126 			KSHORT nAdd = ( nTmp - Height() ) / 2;
127             GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
128             GetRoot().Height( GetRoot().Height() + nAdd );
129 			Height( nTmp );
130 		}
131 		nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent;
132 		if( nTmp > GetAscent() )
133 			SetAscent( nTmp );
134 	}
135 }
136 
137 long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const
138 {
139 	return 0;
140 }
141 
142 sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const
143 {
144     return sal_False;
145 }
146 
147 /*************************************************************************
148  *              virtual SwMultiPortion::HandlePortion()
149  *************************************************************************/
150 
151 void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
152 {
153     rPH.Text( GetLen(), GetWhichPor() );
154 }
155 
156 /*-----------------01.11.00 14:21-------------------
157  * SwMultiPortion::ActualizeTabulator()
158  * sets the tabulator-flag, if there's any tabulator-portion inside.
159  * --------------------------------------------------*/
160 
161 void SwMultiPortion::ActualizeTabulator()
162 {
163 	SwLinePortion* pPor = GetRoot().GetFirstPortion();
164 	// First line
165 	for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() )
166 		if( pPor->InTabGrp() )
167 			SetTab1( sal_True );
168 	if( GetRoot().GetNext() )
169 	{
170 		// Second line
171 		pPor = GetRoot().GetNext()->GetFirstPortion();
172 		do
173 		{
174 			if( pPor->InTabGrp() )
175 				SetTab2( sal_True );
176 			pPor = pPor->GetPortion();
177 		} while ( pPor );
178 	}
179 }
180 
181 /*-----------------16.02.01 12:07-------------------
182  * SwRotatedPortion::SwRotatedPortion(..)
183  * --------------------------------------------------*/
184 
185 SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate,
186     xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd )
187 {
188 	const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem;
189 	if( !pRot )
190 	{
191         const SwTxtAttr& rAttr = *rCreate.pAttr;
192         const SfxPoolItem *const pItem =
193                 CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE);
194         if ( pItem )
195         {
196             pRot = static_cast<const SvxCharRotateItem*>(pItem);
197         }
198 	}
199 	if( pRot )
200     {
201         sal_uInt8 nDir;
202         if ( bRTL )
203             nDir = pRot->IsBottomToTop() ? 3 : 1;
204         else
205             nDir = pRot->IsBottomToTop() ? 1 : 3;
206 
207         SetDirection( nDir );
208     }
209 }
210 
211 /*---------------------------------------------------
212  * SwBidiPortion::SwBidiPortion(..)
213  * --------------------------------------------------*/
214 
215 SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, sal_uInt8 nLv )
216     : SwMultiPortion( nEnd ), nLevel( nLv )
217 {
218     SetBidi();
219 
220     if ( nLevel % 2 )
221         SetDirection( DIR_RIGHT2LEFT );
222     else
223         SetDirection( DIR_LEFT2RIGHT );
224 }
225 
226 
227 long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const
228 {
229     return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR;
230 }
231 
232 sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const
233 {
234 	sal_Bool bRet = sal_False;
235     if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
236     {
237         pCurr->CreateSpaceAdd();
238         pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
239         bRet = sal_True;
240     }
241 
242     return bRet;
243 }
244 
245 xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const
246 {
247     // Calculate number of blanks for justified alignment
248     SwLinePortion* pPor = GetRoot().GetFirstPortion();
249     xub_StrLen nTmpStart = rInf.GetIdx();
250     xub_StrLen nNull = 0;
251     xub_StrLen nBlanks;
252 
253     for( nBlanks = 0; pPor; pPor = pPor->GetPortion() )
254     {
255         if( pPor->InTxtGrp() )
256             nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
257         else if ( pPor->IsMultiPortion() &&
258                  ((SwMultiPortion*)pPor)->IsBidi() )
259             nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf );
260 
261         ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
262     }
263     ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart );
264     return nBlanks;
265 }
266 
267 /*-----------------01.11.00 14:22-------------------
268  * SwDoubleLinePortion::SwDoubleLinePortion(..)
269  * This constructor is for the continuation of a doubleline portion
270  * in the next line.
271  * It takes the same brackets and if the original has no content except
272  * brackets, these will be deleted.
273  * --------------------------------------------------*/
274 
275 SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble,
276                                           xub_StrLen nEnd ) :
277     SwMultiPortion( nEnd ),
278     pBracket( 0 )
279 {
280     SetDirection( rDouble.GetDirection() );
281     SetDouble();
282 	if( rDouble.GetBrackets() )
283 	{
284 		SetBrackets( rDouble );
285 		// An empty multiportion needs no brackets.
286 		// Notice: GetLen() might be zero, if the multiportion contains
287 		// the second part of a field and the width might be zero, if
288 		// it contains a note only. In this cases the brackets are okay.
289 		// But if the length and the width are both zero, the portion
290 		// is really empty.
291 		if( rDouble.Width() ==	rDouble.BracketWidth() )
292 			rDouble.ClearBrackets();
293 	}
294 }
295 
296 /*-----------------01.11.00 14:22-------------------
297  * SwDoubleLinePortion::SwDoubleLinePortion(..)
298  * This constructor uses the textattribut to get the right brackets.
299  * The textattribut could be a 2-line-attribute or a character- or
300  * internetstyle, which contains the 2-line-attribute.
301  * --------------------------------------------------*/
302 
303 SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate,
304 	xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() )
305 {
306 	SetDouble();
307 	const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem;
308 	if( pTwo )
309 		pBracket->nStart = 0;
310 	else
311 	{
312 		const SwTxtAttr& rAttr = *rCreate.pAttr;
313 		pBracket->nStart = *rAttr.GetStart();
314 
315         const SfxPoolItem * const pItem =
316             CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
317         if ( pItem )
318         {
319             pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
320         }
321 	}
322 	if( pTwo )
323 	{
324 		pBracket->cPre = pTwo->GetStartBracket();
325 		pBracket->cPost = pTwo->GetEndBracket();
326 	}
327 	else
328 	{
329 		pBracket->cPre = 0;
330 		pBracket->cPost = 0;
331 	}
332 	sal_uInt8 nTmp = SW_SCRIPTS;
333 	if( pBracket->cPre > 255 )
334 	{
335 		String aTxt( pBracket->cPre );
336         nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
337 	}
338 	pBracket->nPreScript = nTmp;
339 	nTmp = SW_SCRIPTS;
340 	if( pBracket->cPost > 255 )
341 	{
342 		String aTxt( pBracket->cPost );
343         nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
344     }
345 	pBracket->nPostScript = nTmp;
346 
347 	if( !pBracket->cPre && !pBracket->cPost )
348 	{
349 		delete pBracket;
350 		pBracket = 0;
351 	}
352 
353     // double line portions have the same direction as the frame directions
354     if ( rCreate.nLevel % 2 )
355         SetDirection( DIR_RIGHT2LEFT );
356     else
357         SetDirection( DIR_LEFT2RIGHT );
358 }
359 
360 
361 /*-----------------25.10.00 09:51-------------------
362  * SwMultiPortion::PaintBracket paints the wished bracket,
363  * if the multiportion has surrounding brackets.
364  * The X-position of the SwTxtPaintInfo will be modified:
365  * the open bracket sets position behind itself,
366  * the close bracket in front of itself.
367  * --------------------------------------------------*/
368 
369 void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf,
370                                         long nSpaceAdd,
371                                         sal_Bool bOpen ) const
372 {
373 	sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost;
374 	if( !cCh )
375 		return;
376 	KSHORT nChWidth = bOpen ? PreWidth() : PostWidth();
377 	if( !nChWidth )
378 		return;
379 	if( !bOpen )
380 		rInf.X( rInf.X() + Width() - PostWidth() +
381 			( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
382 
383 	SwBlankPortion aBlank( cCh, sal_True );
384 	aBlank.SetAscent( pBracket->nAscent );
385 	aBlank.Width( nChWidth );
386 	aBlank.Height( pBracket->nHeight );
387 	{
388 		SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
389 		sal_uInt8 nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript;
390 		if( SW_SCRIPTS > nAct )
391 			pTmpFnt->SetActual( nAct );
392 		pTmpFnt->SetProportion( 100 );
393 		SwFontSave aSave( rInf, pTmpFnt );
394 		aBlank.Paint( rInf );
395 		delete pTmpFnt;
396 	}
397 	if( bOpen )
398 		rInf.X( rInf.X() + PreWidth() );
399 }
400 
401 /*-----------------25.10.00 16:26-------------------
402  * SwDoubleLinePortion::SetBrackets creates the bracket-structur
403  * and fills it, if not both characters are 0x00.
404  * --------------------------------------------------*/
405 
406 void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
407 {
408 	if( rDouble.pBracket )
409 	{
410 		pBracket = new SwBracket;
411 		pBracket->cPre = rDouble.pBracket->cPre;
412 		pBracket->cPost = rDouble.pBracket->cPost;
413 		pBracket->nPreScript = rDouble.pBracket->nPreScript;
414 		pBracket->nPostScript = rDouble.pBracket->nPostScript;
415 		pBracket->nStart = rDouble.pBracket->nStart;
416 	}
417 }
418 
419 /*-----------------25.10.00 16:29-------------------
420  * SwDoubleLinePortion::FormatBrackets
421  * calculates the size of the brackets => pBracket,
422  * reduces the nMaxWidth-parameter ( minus bracket-width )
423  * and moves the rInf-x-position behind the opening bracket.
424  * --------------------------------------------------*/
425 
426 void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth )
427 {
428 	nMaxWidth -= rInf.X();
429 	SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
430 	pTmpFnt->SetProportion( 100 );
431 	pBracket->nAscent = 0;
432 	pBracket->nHeight = 0;
433 	if( pBracket->cPre )
434 	{
435 		String aStr( pBracket->cPre );
436 		sal_uInt8 nActualScr = pTmpFnt->GetActual();
437 		if( SW_SCRIPTS > pBracket->nPreScript )
438 			pTmpFnt->SetActual( pBracket->nPreScript );
439 		SwFontSave aSave( rInf, pTmpFnt );
440 		SwPosSize aSize = rInf.GetTxtSize( aStr );
441 		pBracket->nAscent = rInf.GetAscent();
442 		pBracket->nHeight = aSize.Height();
443 		pTmpFnt->SetActual( nActualScr );
444 		if( nMaxWidth > aSize.Width() )
445 		{
446 			pBracket->nPreWidth = aSize.Width();
447 			nMaxWidth -= aSize.Width();
448 			rInf.X( rInf.X() + aSize.Width() );
449 		}
450 		else
451 		{
452 			pBracket->nPreWidth = 0;
453 			nMaxWidth = 0;
454 		}
455 	}
456 	else
457 		pBracket->nPreWidth = 0;
458 	if( pBracket->cPost )
459 	{
460 		String aStr( pBracket->cPost );
461 		if( SW_SCRIPTS > pBracket->nPostScript )
462 			pTmpFnt->SetActual( pBracket->nPostScript );
463 		SwFontSave aSave( rInf, pTmpFnt );
464 		SwPosSize aSize = rInf.GetTxtSize( aStr );
465 		KSHORT nTmpAsc = rInf.GetAscent();
466 		if( nTmpAsc > pBracket->nAscent )
467 		{
468 			pBracket->nHeight += nTmpAsc - pBracket->nAscent;
469 			pBracket->nAscent = nTmpAsc;
470 		}
471 		if( aSize.Height() > pBracket->nHeight )
472 			pBracket->nHeight = aSize.Height();
473 		if( nMaxWidth > aSize.Width() )
474 		{
475 			pBracket->nPostWidth = aSize.Width();
476 			nMaxWidth -= aSize.Width();
477 		}
478 		else
479 		{
480 			pBracket->nPostWidth = 0;
481 			nMaxWidth = 0;
482 		}
483 	}
484 	else
485 		pBracket->nPostWidth = 0;
486 	nMaxWidth += rInf.X();
487 }
488 
489 /*-----------------26.10.00 10:36-------------------
490  * SwDoubleLinePortion::CalcBlanks
491  * calculates the number of blanks in each line and
492  * the difference of the width of the two lines.
493  * These results are used from the text adjustment.
494  * --------------------------------------------------*/
495 
496 void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf )
497 {
498 	SwLinePortion* pPor = GetRoot().GetFirstPortion();
499 	xub_StrLen nNull = 0;
500 	xub_StrLen nStart = rInf.GetIdx();
501 	SetTab1( sal_False );
502 	SetTab2( sal_False );
503 	for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() )
504 	{
505 		if( pPor->InTxtGrp() )
506 			nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
507 		rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
508 		if( pPor->InTabGrp() )
509 			SetTab1( sal_True );
510 	}
511 	nLineDiff = GetRoot().Width();
512 	if( GetRoot().GetNext() )
513 	{
514 		pPor = GetRoot().GetNext()->GetFirstPortion();
515 		nLineDiff -= GetRoot().GetNext()->Width();
516 	}
517 	for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() )
518 	{
519 		if( pPor->InTxtGrp() )
520 			nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
521 		rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
522 		if( pPor->InTabGrp() )
523 			SetTab2( sal_True );
524 	}
525 	rInf.SetIdx( nStart );
526 }
527 
528 long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const
529 {
530     return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR;
531 }
532 
533 /*-----------------01.11.00 14:29-------------------
534  * SwDoubleLinePortion::ChangeSpaceAdd(..)
535  * merges the spaces for text adjustment from the inner and outer part.
536  * Inside the doubleline portion the wider line has no spaceadd-array, the
537  * smaller line has such an array to reach width of the wider line.
538  * If the surrounding line has text adjustment and the doubleline portion
539  * contains no tabulator, it is necessary to create/manipulate the inner
540  * space arrays.
541  * --------------------------------------------------*/
542 
543 sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr,
544                                            long nSpaceAdd ) const
545 {
546 	sal_Bool bRet = sal_False;
547 	if( !HasTabulator() && nSpaceAdd > 0 )
548 	{
549         if( !pCurr->IsSpaceAdd() )
550         {
551             // The wider line gets the spaceadd from the surrounding line direct
552 			pCurr->CreateSpaceAdd();
553             pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
554 			bRet = sal_True;
555 		}
556 		else
557 		{
558 			xub_StrLen nMyBlank = GetSmallerSpaceCnt();
559 			xub_StrLen nOther = GetSpaceCnt();
560             SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
561 
562             if( nMyBlank )
563 				nMultiSpace /= nMyBlank;
564 
565             if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR )
566 			{
567 //                pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
568                 // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value,
569                 // instead we want to insert a new first value:
570                 std::vector<long>* pVec = pCurr->GetpLLSpaceAdd();
571                 pVec->insert( pVec->begin(), nMultiSpace );
572                 // <--
573 				bRet = sal_True;
574 			}
575 		}
576 	}
577 	return bRet;
578 }
579 /*-----------------01.11.00 14:29-------------------
580  * SwDoubleLinePortion::ResetSpaceAdd(..)
581  * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
582  * --------------------------------------------------*/
583 
584 void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr )
585 {
586     pCurr->RemoveFirstLLSpaceAdd();;
587     if( !pCurr->GetLLSpaceAddCount() )
588 		pCurr->FinishSpaceAdd();
589 }
590 
591 SwDoubleLinePortion::~SwDoubleLinePortion()
592 {
593 	delete pBracket;
594 }
595 
596 /*-----------------13.11.00 14:50-------------------
597  * SwRubyPortion::SwRubyPortion(..)
598  * constructs a ruby portion, i.e. an additional text is displayed
599  * beside the main text, e.g. phonetic characters.
600  * --------------------------------------------------*/
601 
602 
603 SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) :
604     SwMultiPortion( nEnd ),
605     nRubyOffset( rRuby.GetRubyOffset() ),
606     nAdjustment( rRuby.GetAdjustment() )
607 {
608     SetDirection( rRuby.GetDirection() ),
609     SetTop( rRuby.OnTop() );
610     SetRuby();
611 }
612 
613 /*-----------------13.11.00 14:50-------------------
614  * SwRubyPortion::SwRubyPortion(..)
615  * constructs a ruby portion, i.e. an additional text is displayed
616  * beside the main text, e.g. phonetic characters.
617  * --------------------------------------------------*/
618 
619 SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
620                               const IDocumentSettingAccess& rIDocumentSettingAccess,
621                               xub_StrLen nEnd, xub_StrLen nOffs,
622                               const sal_Bool* pForceRubyPos )
623      : SwMultiPortion( nEnd )
624 {
625 	SetRuby();
626     ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" );
627 	ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
628 	const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby();
629 	nAdjustment = rRuby.GetAdjustment();
630 	nRubyOffset = nOffs;
631 
632     // in grid mode we force the ruby text to the upper or lower line
633     if ( pForceRubyPos )
634         SetTop( *pForceRubyPos );
635     else
636         SetTop( ! rRuby.GetPosition() );
637 
638     const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt();
639 	SwFont *pRubyFont;
640 	if( pFmt )
641 	{
642 		const SwAttrSet& rSet = pFmt->GetAttrSet();
643 	 	pRubyFont = new SwFont( rFnt );
644         pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
645 
646         // we do not allow a vertical font for the ruby text
647         pRubyFont->SetVertical( rFnt.GetOrientation() );
648 	}
649 	else
650 		pRubyFont = NULL;
651 
652 	String aStr( rRuby.GetText(), nOffs, STRING_LEN );
653 	SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont );
654     pFld->SetNextOffset( nOffs );
655 	pFld->SetFollow( sal_True );
656 
657     if( OnTop() )
658 		GetRoot().SetPortion( pFld );
659 	else
660 	{
661 		GetRoot().SetNext( new SwLineLayout() );
662 		GetRoot().GetNext()->SetPortion( pFld );
663 	}
664 
665     // ruby portions have the same direction as the frame directions
666     if ( rCreate.nLevel % 2 )
667     {
668         // switch right and left ruby adjustment in rtl environment
669         if ( 0 == nAdjustment )
670             nAdjustment = 2;
671         else if ( 2 == nAdjustment )
672             nAdjustment = 0;
673 
674         SetDirection( DIR_RIGHT2LEFT );
675     }
676     else
677         SetDirection( DIR_LEFT2RIGHT );
678 }
679 
680 /*-----------------13.11.00 14:56-------------------
681  * SwRubyPortion::_Adjust(..)
682  * In ruby portion there are different alignments for
683  * the ruby text and the main text.
684  * Left, right, centered and two possibilities of block adjustment
685  * The block adjustment is realized by spacing between the characteres,
686  * either with a half space or no space in front of the first letter and
687  * a half space at the end of the last letter.
688  * Notice: the smaller line will be manipulated, normally it's the ruby line,
689  * but it could be the main text, too.
690  * If there is a tabulator in smaller line, no adjustment is possible.
691  * --------------------------------------------------*/
692 
693 void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf )
694 {
695 	SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
696 	xub_StrLen nOldIdx = rInf.GetIdx();
697 	if( !nLineDiff )
698 		return;
699 	SwLineLayout *pCurr;
700 	if( nLineDiff < 0 )
701 	{   // The first line has to be adjusted.
702 		if( GetTab1() )
703 			return;
704 		pCurr = &GetRoot();
705 		nLineDiff = -nLineDiff;
706 	}
707 	else
708 	{   // The second line has to be adjusted.
709 		if( GetTab2() )
710 			return;
711 		pCurr = GetRoot().GetNext();
712 		rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
713 	}
714 	KSHORT nLeft = 0;	// the space in front of the first letter
715 	KSHORT nRight = 0;	// the space at the end of the last letter
716 	sal_uInt16 nSub = 0;
717 	switch ( nAdjustment )
718 	{
719         case 1: nRight = static_cast<sal_uInt16>(nLineDiff / 2);    // no break
720         case 2: nLeft  = static_cast<sal_uInt16>(nLineDiff - nRight); break;
721         case 3: nSub   = 1; // no break
722 		case 4:
723 		{
724 			xub_StrLen nCharCnt = 0;
725 			SwLinePortion *pPor;
726 			for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() )
727 			{
728 				if( pPor->InTxtGrp() )
729 					((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt );
730 				rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
731 			}
732 			if( nCharCnt > nSub )
733 			{
734 				SwTwips nCalc = nLineDiff / ( nCharCnt - nSub );
735                 short nTmp;
736 				if( nCalc < SHRT_MAX )
737 					nTmp = -short(nCalc);
738 				else
739 					nTmp = SHRT_MIN;
740 
741                 pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
742 				nLineDiff -= nCalc * ( nCharCnt - 1 );
743 			}
744 			if( nLineDiff > 1 )
745 			{
746                 nRight = static_cast<sal_uInt16>(nLineDiff / 2);
747                 nLeft  = static_cast<sal_uInt16>(nLineDiff - nRight);
748 			}
749 			break;
750 		}
751 		default: ASSERT( sal_False, "New ruby adjustment" );
752 	}
753 	if( nLeft || nRight )
754 	{
755 		if( !pCurr->GetPortion() )
756 			pCurr->SetPortion( new SwTxtPortion( *pCurr ) );
757 		SwMarginPortion *pMarg = new SwMarginPortion( 0 );
758 		if( nLeft )
759 		{
760 			pMarg->AddPrtWidth( nLeft );
761 			pMarg->SetPortion( pCurr->GetPortion() );
762 			pCurr->SetPortion( pMarg );
763 		}
764 		if( nRight )
765 		{
766 			pMarg = new SwMarginPortion( 0 );
767 			pMarg->AddPrtWidth( nRight );
768 			pCurr->FindLastPortion()->Append( pMarg );
769 		}
770 	}
771 
772     pCurr->Width( Width() );
773 	rInf.SetIdx( nOldIdx );
774 }
775 
776 /*-----------------08.11.00 14:14-------------------
777  * CalcRubyOffset()
778  * has to change the nRubyOffset, if there's a fieldportion
779  * in the phonetic line.
780  * The nRubyOffset is the position in the rubystring, where the
781  * next SwRubyPortion has start the displaying of the phonetics.
782  * --------------------------------------------------*/
783 
784 void SwRubyPortion::CalcRubyOffset()
785 {
786 	const SwLineLayout *pCurr = &GetRoot();
787 	if( !OnTop() )
788 	{
789 		pCurr = pCurr->GetNext();
790 		if( !pCurr )
791 			return;
792 	}
793 	const SwLinePortion *pPor = pCurr->GetFirstPortion();
794 	const SwFldPortion *pFld = NULL;
795 	while( pPor )
796 	{
797 		if( pPor->InFldGrp() )
798 			pFld = (SwFldPortion*)pPor;
799 		pPor = pPor->GetPortion();
800 	}
801 	if( pFld )
802 	{
803 		if( pFld->HasFollow() )
804 			nRubyOffset = pFld->GetNextOffset();
805 		else
806 			nRubyOffset = STRING_LEN;
807 	}
808 }
809 
810 /*-----------------13.10.00 16:22-------------------
811  * SwTxtSizeInfo::GetMultiCreator(..)
812  * If we (e.g. the position rPos) are inside a two-line-attribute or
813  * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
814  * otherwise the function returns zero.
815  * The rPos parameter is set to the end of the multiportion,
816  * normally this is the end of the attribute,
817  * but sometimes it is the start of another attribute, which finished or
818  * interrupts the first attribute.
819  * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
820  * with different brackets interrupts another 2-line-attribute.
821  * --------------------------------------------------*/
822 
823 /*-----------------13.11.00 15:38-------------------
824  * lcl_Has2Lines(..)
825  * is a little help function for GetMultiCreator(..)
826  * It extracts the 2-line-format from a 2-line-attribute or a character style.
827  * The rValue is set to sal_True, if the 2-line-attribute's value is set and
828  * no 2-line-format reference is passed. If there is a 2-line-format reference,
829  * then the rValue is set only, if the 2-line-attribute's value is set _and_
830  * the 2-line-formats has the same brackets.
831  * --------------------------------------------------*/
832 
833 sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef,
834 	sal_Bool &rValue )
835 {
836     const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
837     if( pItem )
838     {
839         rValue = ((SvxTwoLinesItem*)pItem)->GetValue();
840         if( !rpRef )
841             rpRef = (SvxTwoLinesItem*)pItem;
842         else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() !=
843                     rpRef->GetEndBracket() ||
844                     ((SvxTwoLinesItem*)pItem)->GetStartBracket() !=
845                     rpRef->GetStartBracket() )
846             rValue = sal_False;
847         return sal_True;
848     }
849 	return sal_False;
850 }
851 
852 /*-----------------16.02.01 16:39-------------------
853  * lcl_HasRotation(..)
854  * is a little help function for GetMultiCreator(..)
855  * It extracts the charrotation from a charrotate-attribute or a character style.
856  * The rValue is set to sal_True, if the charrotate-attribute's value is set and
857  * no charrotate-format reference is passed.
858  * If there is a charrotate-format reference, then the rValue is set only,
859  * if the charrotate-attribute's value is set _and_ identical
860  * to the charrotate-format's value.
861  * --------------------------------------------------*/
862 
863 sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr,
864 	const SvxCharRotateItem* &rpRef, sal_Bool &rValue )
865 {
866     const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE );
867     if ( pItem )
868 	{
869         rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue();
870         if( !rpRef )
871             rpRef = (SvxCharRotateItem*)pItem;
872         else if( ((SvxCharRotateItem*)pItem)->GetValue() !=
873                     rpRef->GetValue() )
874             rValue = sal_False;
875         return sal_True;
876     }
877 
878 	return sal_False;
879 }
880 
881 SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos,
882                                                 SwMultiPortion* pMulti ) const
883 {
884     SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo();
885 
886     // get the last embedding level
887     sal_uInt8 nCurrLevel;
888     if ( pMulti )
889     {
890         ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" )
891         // level associated with bidi-portion;
892         nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel();
893     }
894     else
895         // no nested bidi portion required
896         nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
897 
898     // check if there is a field at rPos:
899     sal_uInt8 nNextLevel = nCurrLevel;
900     sal_Bool bFldBidi = sal_False;
901 
902     if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) )
903     {
904 		bFldBidi = sal_True;
905 /*
906         // examining the script of the field text should be sufficient
907         // for 99% of all cases
908         XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 );
909 
910         if ( pBreakIt->GetBreakIter().is() && aTxt.Len() )
911         {
912             sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX ==
913                                  pBreakIt->GetRealScriptOfText( aTxt, 0 ) );
914             sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) );
915             if ( bFldDir != bCurrDir )
916             {
917                 nNextLevel = nCurrLevel + 1;
918                 bFldBidi = sal_True;
919             }
920         }*/
921     }
922     else
923         nNextLevel = rSI.DirType( rPos );
924 
925     if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel )
926     {
927         rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel );
928         if ( STRING_LEN == rPos )
929             return NULL;
930         SwMultiCreator *pRet = new SwMultiCreator;
931 		pRet->pItem = NULL;
932         pRet->pAttr = NULL;
933         pRet->nId = SW_MC_BIDI;
934         pRet->nLevel = nCurrLevel + 1;
935 		return pRet;
936     }
937 
938     // a bidi portion can only contain other bidi portions
939     if ( pMulti )
940         return NULL;
941 
942 	const SvxCharRotateItem* pRotate = NULL;
943 	const SfxPoolItem* pRotItem;
944 	if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
945 		GetItemState( RES_CHRATR_ROTATE, sal_True, &pRotItem ) &&
946 		((SvxCharRotateItem*)pRotItem)->GetValue() )
947 		pRotate = (SvxCharRotateItem*)pRotItem;
948 	else
949 		pRotItem = NULL;
950 	const SvxTwoLinesItem* p2Lines = NULL;
951 	const SfxPoolItem* pItem;
952 	if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
953 		GetItemState( RES_CHRATR_TWO_LINES, sal_True, &pItem ) &&
954 		((SvxTwoLinesItem*)pItem)->GetValue() )
955 		p2Lines = (SvxTwoLinesItem*)pItem;
956 	else
957 		pItem = NULL;
958 
959 	const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints();
960 	if( !pHints && !p2Lines && !pRotate )
961 		return NULL;
962 	const SwTxtAttr *pRuby = NULL;
963 	sal_Bool bTwo = sal_False;
964 	sal_Bool bRot = sal_False;
965 	sal_uInt16 n2Lines = USHRT_MAX;
966 	sal_uInt16 nRotate = USHRT_MAX;
967 	sal_uInt16 nCount = pHints ? pHints->Count() : 0;
968 	sal_uInt16 i;
969 	for( i = 0; i < nCount; ++i )
970 	{
971 		const SwTxtAttr *pTmp = (*pHints)[i];
972 		xub_StrLen nStart = *pTmp->GetStart();
973 		if( rPos < nStart )
974 			break;
975 		if( *pTmp->GetAnyEnd() > rPos )
976 		{
977 			if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
978 				pRuby = pTmp;
979 			else
980 			{
981 				const SvxCharRotateItem* pRoTmp = NULL;
982 				if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) )
983 				{
984 					nRotate = bRot ? i : nCount;
985 					pRotate = pRoTmp;
986 				}
987 				const SvxTwoLinesItem* p2Tmp = NULL;
988 				if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) )
989 				{
990 					n2Lines = bTwo ? i : nCount;
991 					p2Lines = p2Tmp;
992 				}
993 			}
994 		}
995 	}
996 	if( pRuby )
997 	{	// The winner is ... a ruby attribute and so
998 		// the end of the multiportion is the end of the ruby attribute.
999 		rPos = *pRuby->GetEnd();
1000 		SwMultiCreator *pRet = new SwMultiCreator;
1001 		pRet->pItem = NULL;
1002 		pRet->pAttr = pRuby;
1003 		pRet->nId = SW_MC_RUBY;
1004         pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1005         return pRet;
1006 	}
1007 	if( n2Lines < nCount || ( pItem && pItem == p2Lines &&
1008 		rPos < GetTxt().Len() ) )
1009 	{	// The winner is a 2-line-attribute,
1010 		// the end of the multiportion depends on the following attributes...
1011 		SwMultiCreator *pRet = new SwMultiCreator;
1012 
1013 		// We note the endpositions of the 2-line attributes in aEnd as stack
1014 		SvXub_StrLens aEnd;
1015 
1016 		// The bOn flag signs the state of the last 2-line attribute in the
1017 		// aEnd-stack, it is compatible with the winner-attribute or
1018 		// it interrupts the other attribute.
1019 		sal_Bool bOn = sal_True;
1020 
1021 		if( n2Lines < nCount )
1022 		{
1023 			pRet->pItem = NULL;
1024 			pRet->pAttr = (*pHints)[n2Lines];
1025 			aEnd.push_front( *pRet->pAttr->GetEnd() );
1026 			if( pItem )
1027 			{
1028 				aEnd.front() = GetTxt().Len();
1029 				bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() ==
1030 						p2Lines->GetEndBracket() &&
1031 					  ((SvxTwoLinesItem*)pItem)->GetStartBracket() ==
1032 						p2Lines->GetStartBracket();
1033 			}
1034 		}
1035 		else
1036 		{
1037 			pRet->pItem = pItem;
1038 			pRet->pAttr = NULL;
1039 			aEnd.push_front( GetTxt().Len() );
1040 		}
1041 		pRet->nId = SW_MC_DOUBLE;
1042         pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1043 
1044 		// n2Lines is the index of the last 2-line-attribute, which contains
1045 		// the actual position.
1046 		i = 0;
1047 		// At this moment we know that at position rPos the "winner"-attribute
1048 		// causes a 2-line-portion. The end of the attribute is the end of the
1049 		// portion, if there's no interrupting attribute.
1050 		// There are two kinds of interruptors:
1051 		// - ruby attributes stops the 2-line-attribute, the end of the
1052 		//	 multiline is the start of the ruby attribute
1053 		// - 2-line-attributes with value "Off" or with different brackets,
1054 		//   these attributes may interrupt the winner, but they could be
1055 		//	 neutralized by another 2-line-attribute starting at the same
1056 		//	 position with the same brackets as the winner-attribute.
1057 
1058 		// In the following loop rPos is the critical position and it will be
1059 		// evaluated, if at rPos starts a interrupting or a maintaining
1060 		// continuity attribute.
1061 		while( i < nCount )
1062 		{
1063 			const SwTxtAttr *pTmp = (*pHints)[i++];
1064 			if( *pTmp->GetAnyEnd() <= rPos )
1065 				continue;
1066 			if( rPos < *pTmp->GetStart() )
1067 			{
1068 				// If bOn is sal_False and the next attribute starts later than rPos
1069 				// the winner attribute is interrupted at rPos.
1070 				// If the start of the next atribute is behind the end of
1071 				// the last attribute on the aEnd-stack, this is the endposition
1072 				// on the stack is the end of the 2-line portion.
1073 				if( !bOn || aEnd.back() < *pTmp->GetStart() )
1074 					break;
1075 				// At this moment, bOn is sal_True and the next attribute starts
1076 				// behind rPos, so we could move rPos to the next startpoint
1077 				rPos = *pTmp->GetStart();
1078 				// We clean up the aEnd-stack, endpositions equal to rPos are
1079 				// superfluous.
1080 				while( !aEnd.empty() && aEnd.back() <= rPos )
1081 				{
1082 					bOn = !bOn;
1083 					aEnd.pop_back();
1084 				}
1085 				// If the endstack is empty, we simulate an attribute with
1086 				// state sal_True and endposition rPos
1087 				if( aEnd.empty() )
1088 				{
1089 					aEnd.push_front( rPos );
1090 					bOn = sal_True;
1091 				}
1092 			}
1093 			// A ruby attribute stops the 2-line immediately
1094 			if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1095 				return pRet;
1096 			if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
1097 			{   // We have an interesting attribute..
1098 				if( bTwo == bOn )
1099 				{   // .. with the same state, so the last attribute could
1100 					// be continued.
1101 					if( aEnd.back() < *pTmp->GetEnd() )
1102 						aEnd.back() = *pTmp->GetEnd();
1103 				}
1104 				else
1105 				{   // .. with a different state.
1106 					bOn = bTwo;
1107 					// If this is smaller than the last on the stack, we put
1108 					// it on the stack. If it has the same endposition, the last
1109 					// could be removed.
1110 					if( aEnd.back() > *pTmp->GetEnd() )
1111 						aEnd.push_back( *pTmp->GetEnd() );
1112 					else if( aEnd.size() > 1 )
1113 						aEnd.pop_back();
1114 					else
1115 						aEnd.back() = *pTmp->GetEnd();
1116 				}
1117 			}
1118 		}
1119 		if( bOn && !aEnd.empty() )
1120 			rPos = aEnd.back();
1121 		return pRet;
1122 	}
1123 	if( nRotate < nCount || ( pRotItem && pRotItem == pRotate &&
1124 		rPos < GetTxt().Len() ) )
1125 	{	// The winner is a rotate-attribute,
1126 		// the end of the multiportion depends on the following attributes...
1127 		SwMultiCreator *pRet = new SwMultiCreator;
1128 		pRet->nId = SW_MC_ROTATE;
1129 
1130 		// We note the endpositions of the 2-line attributes in aEnd as stack
1131 		SvXub_StrLens aEnd;
1132 
1133 		// The bOn flag signs the state of the last 2-line attribute in the
1134 		// aEnd-stack, which could interrupts the winning rotation attribute.
1135 		sal_Bool bOn = pItem ? sal_True : sal_False;
1136 		aEnd.push_front( GetTxt().Len() );
1137 		// n2Lines is the index of the last 2-line-attribute, which contains
1138 		// the actual position.
1139 		i = 0;
1140 		xub_StrLen n2Start = rPos;
1141 		while( i < nCount )
1142 		{
1143 			const SwTxtAttr *pTmp = (*pHints)[i++];
1144 			if( *pTmp->GetAnyEnd() <= n2Start )
1145 				continue;
1146 			if( n2Start < *pTmp->GetStart() )
1147 			{
1148 				if( bOn || aEnd.back() < *pTmp->GetStart() )
1149 					break;
1150 				n2Start = *pTmp->GetStart();
1151 				while( !aEnd.empty() && aEnd.back() <= n2Start )
1152 				{
1153 					bOn = !bOn;
1154 					aEnd.pop_back();
1155 				}
1156 				if( aEnd.empty() )
1157 				{
1158 					aEnd.push_front( n2Start );
1159 					bOn = sal_False;
1160 				}
1161 			}
1162 			// A ruby attribute stops immediately
1163 			if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1164 			{
1165 				bOn = sal_True;
1166 				break;
1167 			}
1168 			p2Lines = NULL;
1169 			if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
1170 			{
1171 				if( bTwo == bOn )
1172 				{
1173 					if( aEnd.back() < *pTmp->GetEnd() )
1174 						aEnd.back() = *pTmp->GetEnd();
1175 				}
1176 				else
1177 				{
1178 					bOn = bTwo;
1179 					if( aEnd.back() > *pTmp->GetEnd() )
1180 						aEnd.push_back( *pTmp->GetEnd() );
1181 					else if( aEnd.size() > 1 )
1182 						aEnd.pop_back();
1183 					else
1184 						aEnd.back() = *pTmp->GetEnd();
1185 				}
1186 			}
1187 		}
1188 		if( !bOn && !aEnd.empty() )
1189 			n2Start = aEnd.back();
1190 
1191 		if( !aEnd.empty() )
1192 			aEnd.clear();
1193 
1194 		bOn = sal_True;
1195 		if( nRotate < nCount )
1196 		{
1197 			pRet->pItem = NULL;
1198 			pRet->pAttr = (*pHints)[nRotate];
1199 			aEnd.push_front( *pRet->pAttr->GetEnd() );
1200 			if( pRotItem )
1201 			{
1202 				aEnd.front() = GetTxt().Len();
1203 				bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() ==
1204 						pRotate->GetValue();
1205 			}
1206 		}
1207 		else
1208 		{
1209 			pRet->pItem = pRotItem;
1210 			pRet->pAttr = NULL;
1211 			aEnd.push_front( GetTxt().Len() );
1212 		}
1213 		i = 0;
1214 		while( i < nCount )
1215 		{
1216 			const SwTxtAttr *pTmp = (*pHints)[i++];
1217 			if( *pTmp->GetAnyEnd() <= rPos )
1218 				continue;
1219 			if( rPos < *pTmp->GetStart() )
1220 			{
1221 				if( !bOn || aEnd.back() < *pTmp->GetStart() )
1222 					break;
1223 				rPos = *pTmp->GetStart();
1224 				while( !aEnd.empty() && aEnd.back() <= rPos )
1225 				{
1226 					bOn = !bOn;
1227 					aEnd.pop_back();
1228 				}
1229 				if( aEnd.empty() )
1230 				{
1231 					aEnd.push_front( rPos );
1232 					bOn = sal_True;
1233 				}
1234 			}
1235 			if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1236 			{
1237 				bOn = sal_False;
1238 				break;
1239 			}
1240 			if( lcl_HasRotation( *pTmp, pRotate, bTwo ) )
1241 			{
1242 				if( bTwo == bOn )
1243 				{
1244 					if( aEnd.back() < *pTmp->GetEnd() )
1245 						aEnd.back() = *pTmp->GetEnd();
1246 				}
1247 				else
1248 				{
1249 					bOn = bTwo;
1250 					if( aEnd.back() > *pTmp->GetEnd() )
1251 						aEnd.push_back( *pTmp->GetEnd() );
1252 					else if( aEnd.size() > 1 )
1253 						aEnd.pop_back();
1254 					else
1255 						aEnd.back() = *pTmp->GetEnd();
1256 				}
1257 			}
1258 		}
1259 		if( bOn && !aEnd.empty() )
1260 			rPos = aEnd.back();
1261 		if( rPos > n2Start )
1262 			rPos = n2Start;
1263 		return pRet;
1264 	}
1265 	return NULL;
1266 }
1267 
1268 /*-----------------01.11.00 14:52-------------------
1269  * SwSpaceManipulator
1270  * is a little helper class to manage the spaceadd-arrays of the text adjustment
1271  * during a PaintMultiPortion.
1272  * The constructor prepares the array for the first line of multiportion,
1273  * the SecondLine-function restores the values for the first line and prepares
1274  * the second line.
1275  * The destructor restores the values of the last manipulation.
1276  * --------------------------------------------------*/
1277 
1278 class SwSpaceManipulator
1279 {
1280 	SwTxtPaintInfo& rInfo;
1281 	SwMultiPortion& rMulti;
1282     std::vector<long>* pOldSpaceAdd;
1283 	MSHORT nOldSpIdx;
1284     long nSpaceAdd;
1285 	sal_Bool bSpaceChg	: 1;
1286 	sal_uInt8 nOldDir	: 2;
1287 public:
1288 	SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult );
1289 	~SwSpaceManipulator();
1290 	void SecondLine();
1291     inline long GetSpaceAdd() const { return nSpaceAdd; }
1292 };
1293 
1294 SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf,
1295                                         SwMultiPortion& rMult ) :
1296          rInfo( rInf ), rMulti( rMult )
1297 {
1298 	pOldSpaceAdd = rInfo.GetpSpaceAdd();
1299 	nOldSpIdx = rInfo.GetSpaceIdx();
1300 	nOldDir = rInfo.GetDirection();
1301 	rInfo.SetDirection( rMulti.GetDirection() );
1302 	bSpaceChg = sal_False;
1303 
1304     if( rMulti.IsDouble() )
1305 	{
1306 		nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ?
1307 					  rInfo.GetSpaceAdd() : 0;
1308         if( rMulti.GetRoot().IsSpaceAdd() )
1309 		{
1310             rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1311 			rInfo.ResetSpaceIdx();
1312 			bSpaceChg = rMulti.ChgSpaceAdd(	&rMulti.GetRoot(), nSpaceAdd );
1313 		}
1314 		else if( rMulti.HasTabulator() )
1315             rInfo.SetpSpaceAdd( NULL );
1316 	}
1317     else if ( ! rMulti.IsBidi() )
1318     {
1319         rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1320 		rInfo.ResetSpaceIdx();
1321 	}
1322 }
1323 
1324 void SwSpaceManipulator::SecondLine()
1325 {
1326 	if( bSpaceChg )
1327 	{
1328         rInfo.RemoveFirstSpaceAdd();
1329 		bSpaceChg = sal_False;
1330 	}
1331 	SwLineLayout *pLay = rMulti.GetRoot().GetNext();
1332     if( pLay->IsSpaceAdd() )
1333 	{
1334         rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() );
1335 		rInfo.ResetSpaceIdx();
1336 		bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd );
1337 	}
1338 	else
1339 	{
1340         rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ?
1341 								0 : pOldSpaceAdd );
1342 		rInfo.SetSpaceIdx( nOldSpIdx);
1343 	}
1344 }
1345 
1346 SwSpaceManipulator::~SwSpaceManipulator()
1347 {
1348 	if( bSpaceChg )
1349 	{
1350         rInfo.RemoveFirstSpaceAdd();
1351 		bSpaceChg = sal_False;
1352 	}
1353     rInfo.SetpSpaceAdd( pOldSpaceAdd );
1354 	rInfo.SetSpaceIdx( nOldSpIdx);
1355 	rInfo.SetDirection( nOldDir );
1356 }
1357 
1358 /*-----------------13.10.00 16:24-------------------
1359  * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion.
1360  * External, for the calling function, it seems to be a normal Paint-function,
1361  * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines
1362  * --------------------------------------------------*/
1363 
1364 void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint,
1365     SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
1366 {
1367     GETGRID( pFrm->FindPageFrm() )
1368     const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
1369     sal_uInt16 nGridWidth = 0;
1370     sal_uInt16 nRubyHeight = 0;
1371     sal_Bool bRubyTop = sal_False;
1372 
1373     if ( bHasGrid )
1374     {
1375         nGridWidth = pGrid->GetBaseHeight();
1376         nRubyHeight = pGrid->GetRubyHeight();
1377         bRubyTop = ! pGrid->GetRubyTextBelow();
1378     }
1379 
1380     // do not allow grid mode for first line in ruby portion
1381     const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
1382 
1383     const sal_uInt16 nOldHeight = rMulti.Height();
1384     const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1385 
1386     if ( bRubyInGrid )
1387     {
1388         GetInfo().SetSnapToGrid( ! bRubyTop );
1389         rMulti.Height( pCurr->Height() );
1390     }
1391 
1392     SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1393     sal_uInt8 nEnvDir = 0;
1394     sal_uInt8 nThisDir = 0;
1395     sal_uInt8 nFrmDir = 0;
1396     if ( rMulti.IsBidi() )
1397     {
1398         // these values are needed for the calculation of the x coordinate
1399         // and the layout mode
1400         ASSERT( ! pEnvPor || pEnvPor->IsBidi(),
1401                 "Oh no, I expected a BidiPortion" )
1402         nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1403         nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir;
1404         nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2;
1405     }
1406 
1407 #if OSL_DEBUG_LEVEL > 1
1408     // only paint first level bidi portions
1409     if( rMulti.Width() > 1 && ! pEnvPor )
1410         GetInfo().DrawViewOpt( rMulti, POR_FLD );
1411 #endif
1412 
1413     if ( bRubyInGrid )
1414         rMulti.Height( nOldHeight );
1415 
1416     // do we have to repaint a post it portion?
1417     if( GetInfo().OnWin() && rMulti.GetPortion() &&
1418         ! rMulti.GetPortion()->Width() )
1419         rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti );
1420 
1421 	// old values must be saved and restored at the end
1422 	xub_StrLen nOldLen = GetInfo().GetLen();
1423 	KSHORT nOldX = KSHORT(GetInfo().X());
1424     long nOldY = GetInfo().Y();
1425 	xub_StrLen nOldIdx = GetInfo().GetIdx();
1426 
1427 	SwSpaceManipulator aManip( GetInfo(), rMulti );
1428 
1429 	SwFontSave *pFontSave;
1430 	SwFont* pTmpFnt;
1431 
1432 	if( rMulti.IsDouble() )
1433 	{
1434 		pTmpFnt = new SwFont( *GetInfo().GetFont() );
1435 		if( rMulti.IsDouble() )
1436 		{
1437 			SetPropFont( 50 );
1438 			pTmpFnt->SetProportion( GetPropFont() );
1439 		}
1440 		pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this );
1441 	}
1442 	else
1443 	{
1444 		pFontSave = NULL;
1445 		pTmpFnt = NULL;
1446 	}
1447 
1448 	if( rMulti.HasBrackets() )
1449 	{
1450         xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
1451 		GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
1452 		SeekAndChg( GetInfo() );
1453 		((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True );
1454         GetInfo().SetIdx( nTmpOldIdx );
1455 	}
1456 
1457 	KSHORT nTmpX = KSHORT(GetInfo().X());
1458 
1459 	SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
1460 	SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
1461     SwTwips nOfst = 0;
1462 
1463     // GetInfo().Y() is the baseline from the surrounding line. We must switch
1464 	// this temporary to the baseline of the inner lines of the multiportion.
1465     if( rMulti.HasRotation() )
1466     {
1467         if( rMulti.IsRevers() )
1468         {
1469             GetInfo().Y( nOldY - rMulti.GetAscent() );
1470             nOfst = nTmpX + rMulti.Width();
1471         }
1472         else
1473         {
1474             GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1475             nOfst = nTmpX;
1476         }
1477     }
1478     else if ( rMulti.IsBidi() )
1479     {
1480         // does the current bidi portion has the same direction
1481         // as its environment?
1482         if ( nEnvDir != nThisDir )
1483         {
1484             // different directions, we have to adjust the x coordinate
1485             SwTwips nMultiWidth = rMulti.Width() +
1486                     rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
1487 
1488             if ( nFrmDir == nThisDir )
1489                 GetInfo().X( GetInfo().X() - nMultiWidth );
1490             else
1491                 GetInfo().X( GetInfo().X() + nMultiWidth );
1492         }
1493 
1494         nOfst = nOldY - rMulti.GetAscent();
1495 
1496         // set layout mode
1497         aLayoutModeModifier.Modify( nThisDir );
1498     }
1499     else
1500         nOfst = nOldY - rMulti.GetAscent();
1501 
1502     sal_Bool bRest = pLay->IsRest();
1503 	sal_Bool bFirst = sal_True;
1504 
1505     ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
1506             " Only BiDi portions are allowed to use the common underlining font" )
1507 
1508     do
1509 	{
1510         if ( bHasGrid )
1511         {
1512             if( rMulti.HasRotation() )
1513             {
1514                 const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
1515                                             pPor->GetAscent();
1516                 if( rMulti.IsRevers() )
1517                     GetInfo().X( nOfst - nAdjustment );
1518                 else
1519                     GetInfo().X( nOfst + nAdjustment );
1520             }
1521             else
1522             {
1523                 // special treatment for ruby portions in grid mode
1524                 SwTwips nAdjustment = 0;
1525                 if ( rMulti.IsRuby() )
1526                 {
1527                     if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
1528                         // adjust base text
1529                         nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
1530                     else if ( bRubyTop )
1531                         // adjust upper ruby text
1532                         nAdjustment = nRubyHeight - pPor->Height();
1533                     // else adjust lower ruby text
1534                 }
1535 
1536                 GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
1537             }
1538         }
1539         else if( rMulti.HasRotation() )
1540         {
1541             if( rMulti.IsRevers() )
1542                 GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) );
1543             else
1544                 GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
1545         }
1546         else
1547             GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
1548 
1549         sal_Bool bSeeked = sal_True;
1550 		GetInfo().SetLen( pPor->GetLen() );
1551 
1552         if( bRest && pPor->InFldGrp() && !pPor->GetLen() )
1553 		{
1554 			if(	((SwFldPortion*)pPor)->HasFont() )
1555 		 		bSeeked = sal_False;
1556 			else
1557 				SeekAndChgBefore( GetInfo() );
1558 		}
1559 		else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() )
1560 			SeekAndChg( GetInfo() );
1561 		else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
1562 		{
1563 			if( GetRedln() )
1564 				SeekAndChg( GetInfo() );
1565 			else
1566 				SeekAndChgBefore( GetInfo() );
1567 		}
1568 		else
1569             bSeeked = sal_False;
1570 
1571 		SwLinePortion *pNext = pPor->GetPortion();
1572 		if(GetInfo().OnWin() && pNext && !pNext->Width() )
1573 		{
1574 			if ( !bSeeked )
1575 				SeekAndChg( GetInfo() );
1576             pNext->PrePaint( GetInfo(), pPor );
1577 		}
1578 
1579         CheckSpecialUnderline( pPor );
1580         SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
1581         if ( pUnderLineFnt )
1582         {
1583             if ( rMulti.IsDouble() )
1584                 pUnderLineFnt->GetFont().SetProportion( 50 );
1585             pUnderLineFnt->SetPos( GetInfo().GetPos() );
1586         }
1587 
1588         if ( rMulti.IsBidi() )
1589         {
1590             // we do not allow any rotation inside a bidi portion
1591             SwFont* pTmpFont = GetInfo().GetFont();
1592             pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() );
1593         }
1594 
1595         if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() )
1596         {
1597             // but we do allow nested bidi portions
1598             ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" )
1599             PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti );
1600         }
1601         else
1602             pPor->Paint( GetInfo() );
1603 
1604         if( GetFnt()->IsURL() && pPor->InTxtGrp() )
1605 			GetInfo().NotifyURL( *pPor );
1606 
1607 		bFirst &= !pPor->GetLen();
1608 		if( pNext || !pPor->IsMarginPortion() )
1609 			pPor->Move( GetInfo() );
1610 
1611         pPor = pNext;
1612 
1613 		// If there's no portion left, we go to the next line
1614 		if( !pPor && pLay->GetNext() )
1615 		{
1616             pLay = pLay->GetNext();
1617             pPor = pLay->GetFirstPortion();
1618             bRest = pLay->IsRest();
1619             aManip.SecondLine();
1620 
1621             // delete underline font
1622             delete GetInfo().GetUnderFnt();
1623             GetInfo().SetUnderFnt( 0 );
1624 
1625             if( rMulti.HasRotation() )
1626             {
1627                 if( rMulti.IsRevers() )
1628                 {
1629                     nOfst += pLay->Height();
1630                     GetInfo().Y( nOldY - rMulti.GetAscent() );
1631                 }
1632                 else
1633                 {
1634                     nOfst -= pLay->Height();
1635                     GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1636                 }
1637             }
1638             else if ( bHasGrid && rMulti.IsRuby() )
1639             {
1640                 GetInfo().X( nTmpX );
1641                 if ( bRubyTop )
1642                 {
1643                     nOfst += nRubyHeight;
1644                     GetInfo().SetSnapToGrid( sal_True );
1645                 }
1646                 else
1647                 {
1648                     nOfst += pCurr->Height() - nRubyHeight;
1649                     GetInfo().SetSnapToGrid( sal_False );
1650                 }
1651             } else
1652             {
1653                 GetInfo().X( nTmpX );
1654                 // We switch to the baseline of the next inner line
1655                 nOfst += rMulti.GetRoot().Height();
1656             }
1657 		}
1658 	} while( pPor );
1659 
1660     if ( bRubyInGrid )
1661         GetInfo().SetSnapToGrid( bOldGridModeAllowed );
1662 
1663     // delete underline font
1664     if ( ! rMulti.IsBidi() )
1665     {
1666         delete GetInfo().GetUnderFnt();
1667         GetInfo().SetUnderFnt( 0 );
1668     }
1669 
1670     GetInfo().SetIdx( nOldIdx );
1671 	GetInfo().Y( nOldY );
1672 
1673 	if( rMulti.HasBrackets() )
1674 	{
1675         xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
1676 		GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
1677 		SeekAndChg( GetInfo() );
1678 		GetInfo().X( nOldX );
1679 		((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(),
1680 			aManip.GetSpaceAdd(), sal_False );
1681         GetInfo().SetIdx( nTmpOldIdx );
1682 	}
1683 	// Restore the saved values
1684 	GetInfo().X( nOldX );
1685 	GetInfo().SetLen( nOldLen );
1686 	delete pFontSave;
1687 	delete pTmpFnt;
1688 	SetPropFont( 0 );
1689 }
1690 
1691 sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld )
1692 {
1693 	SwLinePortion* pLast = pLine;
1694 	rpFld = pLine->GetPortion();
1695 	while( rpFld && !rpFld->InFldGrp() )
1696 	{
1697 		pLast = rpFld;
1698 		rpFld = rpFld->GetPortion();
1699 	}
1700 	sal_Bool bRet = rpFld != 0;
1701 	if( bRet )
1702 	{
1703 		if( ((SwFldPortion*)rpFld)->IsFollow() )
1704 		{
1705 			rpFld->Truncate();
1706 			pLast->SetPortion( NULL );
1707 		}
1708 		else
1709 			rpFld = NULL;
1710 	}
1711 	pLine->Truncate();
1712 	return bRet;
1713 }
1714 
1715 /*----------------------------------------------------
1716  *              lcl_TruncateMultiPortion
1717  * If a multi portion completely has to go to the
1718  * next line, this function is called to trunctate
1719  * the rest of the remaining multi portion
1720  * --------------------------------------------------*/
1721 
1722 void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf,
1723                                xub_StrLen nStartIdx )
1724 {
1725     rMulti.GetRoot().Truncate();
1726     rMulti.GetRoot().SetLen(0);
1727     rMulti.GetRoot().Width(0);
1728 //  rMulti.CalcSize( *this, aInf );
1729     if ( rMulti.GetRoot().GetNext() )
1730     {
1731         rMulti.GetRoot().GetNext()->Truncate();
1732         rMulti.GetRoot().GetNext()->SetLen( 0 );
1733         rMulti.GetRoot().GetNext()->Width( 0 );
1734     }
1735     rMulti.Width( 0 );
1736     rMulti.SetLen(0);
1737     rInf.SetIdx( nStartIdx );
1738 }
1739 
1740 /*-----------------------------------------------------------------------------
1741  *              SwTxtFormatter::BuildMultiPortion
1742  * manages the formatting of a SwMultiPortion. External, for the calling
1743  * function, it seems to be a normal Format-function, internal it is like a
1744  * SwTxtFrm::_Format with multiple BuildPortions
1745  *---------------------------------------------------------------------------*/
1746 
1747 sal_Bool SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf,
1748 	SwMultiPortion& rMulti )
1749 {
1750 	SwTwips nMaxWidth = rInf.Width();
1751     KSHORT nOldX = 0;
1752 
1753 	if( rMulti.HasBrackets() )
1754 	{
1755 		xub_StrLen nOldIdx = rInf.GetIdx();
1756 		rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart );
1757 		SeekAndChg( rInf );
1758         nOldX = KSHORT(GetInfo().X());
1759 		((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth );
1760 		rInf.SetIdx( nOldIdx );
1761 	}
1762 
1763 	SeekAndChg( rInf );
1764 	SwFontSave *pFontSave;
1765 	if( rMulti.IsDouble() )
1766 	{
1767 		SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
1768 		if( rMulti.IsDouble() )
1769 		{
1770 			SetPropFont( 50 );
1771 			pTmpFnt->SetProportion( GetPropFont() );
1772 		}
1773 		pFontSave = new SwFontSave( rInf, pTmpFnt, this );
1774 	}
1775 	else
1776 		pFontSave = NULL;
1777 
1778     SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1779     if ( rMulti.IsBidi() )
1780     {
1781         // set layout mode
1782         aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() );
1783     }
1784 
1785     SwTwips nTmpX = 0;
1786 
1787     if( rMulti.HasRotation() )
1788     {
1789         // For nMaxWidth we take the height of the body frame.
1790         // #i25067#: If the current frame is inside a table, we restrict
1791         // nMaxWidth to the current frame height, unless the frame size
1792         // attribute is set to variable size:
1793 
1794         // We set nTmpX (which is used for portion calculating) to the
1795         // current Y value
1796         const SwPageFrm* pPage = pFrm->FindPageFrm();
1797         ASSERT( pPage, "No page in frame!");
1798         const SwLayoutFrm* pUpperFrm = pPage;
1799 
1800         if ( pFrm->IsInTab() )
1801         {
1802             pUpperFrm = pFrm->GetUpper();
1803             while ( pUpperFrm && !pUpperFrm->IsCellFrm() )
1804                 pUpperFrm = pUpperFrm->GetUpper();
1805             ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" )
1806             const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine();
1807             const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize();
1808             if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() )
1809                 pUpperFrm = pPage;
1810         }
1811         if ( pUpperFrm == pPage && !pFrm->IsInFtn() )
1812             pUpperFrm = pPage->FindBodyCont();
1813 
1814         nMaxWidth = pUpperFrm ?
1815                     ( rInf.GetTxtFrm()->IsVertical() ?
1816                       pUpperFrm->Prt().Width() :
1817                       pUpperFrm->Prt().Height() ) :
1818                     USHRT_MAX;
1819     }
1820     else
1821         nTmpX = rInf.X();
1822 
1823     SwMultiPortion* pOldMulti = pMulti;
1824 
1825     pMulti = &rMulti;
1826 	SwLineLayout *pOldCurr = pCurr;
1827 	xub_StrLen nOldStart = GetStart();
1828 	SwTwips nMinWidth = nTmpX + 1;
1829     SwTwips nActWidth = nMaxWidth;
1830     const xub_StrLen nStartIdx = rInf.GetIdx();
1831 	xub_StrLen nMultiLen = rMulti.GetLen();
1832 
1833 	SwLinePortion *pFirstRest;
1834 	SwLinePortion *pSecondRest;
1835 	if( rMulti.IsFormatted() )
1836 	{
1837 		if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
1838 			&& rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1839 			lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
1840 		if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1841 			lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
1842 		else
1843 			pSecondRest = NULL;
1844 	}
1845 	else
1846 	{
1847 		pFirstRest = rMulti.GetRoot().GetPortion();
1848 		pSecondRest = rMulti.GetRoot().GetNext() ?
1849 					  rMulti.GetRoot().GetNext()->GetPortion() : NULL;
1850 		if( pFirstRest )
1851 			rMulti.GetRoot().SetPortion( NULL );
1852 		if( pSecondRest )
1853 			rMulti.GetRoot().GetNext()->SetPortion( NULL );
1854 		rMulti.SetFormatted();
1855 		nMultiLen = nMultiLen - rInf.GetIdx();
1856 	}
1857 
1858     // save some values
1859 	const XubString* pOldTxt = &(rInf.GetTxt());
1860     const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
1861 
1862 	XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() );
1863 	rInf.SetTxt( aMultiStr );
1864 	SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
1865     // Do we allow break cuts? The FirstMulti-Flag is evaluated during
1866     // line break determination.
1867     sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
1868 
1869 	SwLinePortion *pNextFirst = NULL;
1870 	SwLinePortion *pNextSecond = NULL;
1871 	sal_Bool bRet = sal_False;
1872 
1873     GETGRID( pFrm->FindPageFrm() )
1874     const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
1875 
1876     sal_uInt16 nGridWidth = 0;
1877     sal_uInt16 nRubyHeight = 0;
1878     sal_Bool bRubyTop = sal_False;
1879 
1880     if ( bHasGrid )
1881     {
1882         nGridWidth = pGrid->GetBaseHeight();
1883         nRubyHeight = pGrid->GetRubyHeight();
1884         bRubyTop = ! pGrid->GetRubyTextBelow();
1885     }
1886 
1887 	do
1888 	{
1889 		pCurr = &rMulti.GetRoot();
1890 		nStart = nStartIdx;
1891 		bRet = sal_False;
1892 		FormatReset( aInf );
1893 		aInf.X( nTmpX );
1894 		aInf.Width( KSHORT(nActWidth) );
1895 		aInf.RealWidth( KSHORT(nActWidth) );
1896         aInf.SetFirstMulti( bFirstMulti );
1897         aInf.SetNumDone( rInf.IsNumDone() );
1898         aInf.SetFtnDone( rInf.IsFtnDone() );
1899 
1900         if( pFirstRest )
1901 		{
1902             ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected");
1903 			SwFldPortion *pFld =
1904 				((SwFldPortion*)pFirstRest)->Clone(
1905 					((SwFldPortion*)pFirstRest)->GetExp() );
1906 			pFld->SetFollow( sal_True );
1907 			aInf.SetRest( pFld );
1908 		}
1909 		aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
1910 
1911         // in grid mode we temporarily have to disable the grid for the ruby line
1912         const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1913         if ( bHasGrid && aInf.IsRuby() && bRubyTop )
1914             aInf.SetSnapToGrid( sal_False );
1915 
1916         // If there's no more rubytext, then buildportion is forbidden
1917 		if( pFirstRest || !aInf.IsRuby() )
1918 			BuildPortions( aInf );
1919 
1920         aInf.SetSnapToGrid( bOldGridModeAllowed );
1921 
1922 		rMulti.CalcSize( *this, aInf );
1923         pCurr->SetRealHeight( pCurr->Height() );
1924 
1925         if( rMulti.IsBidi() )
1926         {
1927             pNextFirst = aInf.GetRest();
1928             break;
1929         }
1930 
1931 		if( rMulti.HasRotation() && !rMulti.IsDouble() )
1932 			break;
1933         // second line has to be formatted
1934         else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
1935 		{
1936 			xub_StrLen nFirstLen = pCurr->GetLen();
1937 			delete pCurr->GetNext();
1938 			pCurr->SetNext( new SwLineLayout() );
1939             pCurr = pCurr->GetNext();
1940 			nStart = aInf.GetIdx();
1941 			aInf.X( nTmpX );
1942 			SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth );
1943 			if( rMulti.IsRuby() )
1944 			{
1945 				aTmp.SetRuby( !rMulti.OnTop() );
1946 				pNextFirst = aInf.GetRest();
1947 				if( pSecondRest )
1948 				{
1949                     ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected");
1950 					SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone(
1951 									((SwFldPortion*)pSecondRest)->GetExp() );
1952 					pFld->SetFollow( sal_True );
1953 					aTmp.SetRest( pFld );
1954 				}
1955 				if( !rMulti.OnTop() && nFirstLen < nMultiLen )
1956 					bRet = sal_True;
1957 			}
1958 			else
1959 				aTmp.SetRest( aInf.GetRest() );
1960 			aInf.SetRest( NULL );
1961 
1962             // in grid mode we temporarily have to disable the grid for the ruby line
1963             if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
1964                 aTmp.SetSnapToGrid( sal_False );
1965 
1966             BuildPortions( aTmp );
1967 
1968             aTmp.SetSnapToGrid( bOldGridModeAllowed );
1969 
1970             rMulti.CalcSize( *this, aInf );
1971             rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
1972 			pCurr->SetRealHeight( pCurr->Height() );
1973 			if( rMulti.IsRuby() )
1974 			{
1975 				pNextSecond = aTmp.GetRest();
1976 				if( pNextFirst )
1977 					bRet = sal_True;
1978 			}
1979 			else
1980 				pNextFirst = aTmp.GetRest();
1981 			if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen )
1982 				|| aTmp.GetRest() )
1983                 // our guess for width of multiportion was too small,
1984                 // text did not fit into multiportion
1985 				bRet = sal_True;
1986 		}
1987         if( rMulti.IsRuby() )
1988             break;
1989 		if( bRet )
1990 		{
1991             // our guess for multiportion width was too small,
1992             // we set min to act
1993 			nMinWidth = nActWidth;
1994 			nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
1995             if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
1996             // we have too less space, we must allow break cuts
1997             // ( the first multi flag is considered during TxtPortion::_Format() )
1998                 bFirstMulti = sal_False;
1999             if( nActWidth <= nMinWidth )
2000 				break;
2001 		}
2002 		else
2003         {
2004             // For Solaris, this optimisation can causes trouble:
2005             // Setting this to the portion width ( = rMulti.Width() )
2006             // can make GetTextBreak inside SwTxtGuess::Guess return to small
2007             // values. Therefore we add some extra twips.
2008             if( nActWidth > nTmpX + rMulti.Width() + 6 )
2009                 nActWidth = nTmpX + rMulti.Width() + 6;
2010             nMaxWidth = nActWidth;
2011 			nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
2012 			if( nActWidth >= nMaxWidth )
2013 				break;
2014             // we do not allow break cuts during formatting
2015             bFirstMulti = sal_True;
2016 		}
2017 		delete pNextFirst;
2018 		pNextFirst = NULL;
2019 	} while ( sal_True );
2020 
2021     pMulti = pOldMulti;
2022 
2023 	pCurr = pOldCurr;
2024 	nStart = nOldStart;
2025   	SetPropFont( 0 );
2026 
2027 	rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
2028 		rMulti.GetRoot().GetNext()->GetLen() : 0 ) );
2029 
2030 	if( rMulti.IsDouble() )
2031 	{
2032 		((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf );
2033 		if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() )
2034 		{
2035 			SwLineLayout* pLine = &rMulti.GetRoot();
2036 			if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 )
2037 			{
2038 				rInf.SetIdx( nStartIdx + pLine->GetLen() );
2039 				pLine = pLine->GetNext();
2040 			}
2041 			if( pLine )
2042 			{
2043 				GetInfo().SetMulti( sal_True );
2044 				CalcNewBlock( pLine, NULL, rMulti.Width() );
2045 				GetInfo().SetMulti( sal_False );
2046 			}
2047 			rInf.SetIdx( nStartIdx );
2048 		}
2049 		if( ((SwDoubleLinePortion&)rMulti).GetBrackets() )
2050         {
2051             rMulti.Width( rMulti.Width() +
2052                     ((SwDoubleLinePortion&)rMulti).BracketWidth() );
2053             GetInfo().X( nOldX );
2054         }
2055 	}
2056 	else
2057 	{
2058 		rMulti.ActualizeTabulator();
2059 		if( rMulti.IsRuby() )
2060 		{
2061 			((SwRubyPortion&)rMulti).Adjust( rInf );
2062 			((SwRubyPortion&)rMulti).CalcRubyOffset();
2063 		}
2064 	}
2065 	if( rMulti.HasRotation() )
2066 	{
2067 		SwTwips nH = rMulti.Width();
2068 		SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
2069 		if( nAsc > nH )
2070 			nAsc = nH;
2071 		else if( nAsc < 0 )
2072 			nAsc = 0;
2073 		rMulti.Width( rMulti.Height() );
2074 		rMulti.Height( KSHORT(nH) );
2075 		rMulti.SetAscent( KSHORT(nAsc) );
2076         bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
2077                  nStartIdx != rInf.GetLineStart();
2078 	}
2079     else if ( rMulti.IsBidi() )
2080     {
2081         bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
2082     }
2083 
2084 	// line break has to be performed!
2085     if( bRet )
2086     {
2087         ASSERT( !pNextFirst || pNextFirst->InFldGrp(),
2088             "BuildMultiPortion: Surprising restportion, field expected" );
2089         SwMultiPortion *pTmp;
2090         if( rMulti.IsDouble() )
2091             pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti),
2092                                             nMultiLen + rInf.GetIdx() );
2093         else if( rMulti.IsRuby() )
2094         {
2095             ASSERT( !pNextSecond || pNextSecond->InFldGrp(),
2096                 "BuildMultiPortion: Surprising restportion, field expected" );
2097 
2098             if ( rInf.GetIdx() == rInf.GetLineStart() )
2099             {
2100                 // the ruby portion has to be split in two portions
2101                 pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti),
2102                                           nMultiLen + rInf.GetIdx() );
2103 
2104                 if( pNextSecond )
2105                 {
2106                     pTmp->GetRoot().SetNext( new SwLineLayout() );
2107                     pTmp->GetRoot().GetNext()->SetPortion( pNextSecond );
2108                 }
2109                 pTmp->SetFollowFld();
2110             }
2111             else
2112             {
2113                 // we try to keep our ruby portion together
2114                 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2115                 pTmp = 0;
2116             }
2117         }
2118         else if( rMulti.HasRotation() )
2119         {
2120             // we try to keep our rotated portion together
2121             lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2122             pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
2123                                          rMulti.GetDirection() );
2124         }
2125         // during a recursion of BuildMultiPortions we may not build
2126         // a new SwBidiPortion, this would cause a memory leak
2127         else if( rMulti.IsBidi() && ! pMulti )
2128 		{
2129             if ( ! rMulti.GetLen() )
2130                 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2131 
2132             // If there is a HolePortion at the end of the bidi portion,
2133             // it has to be moved behind the bidi portion. Otherwise
2134             // the visual cursor travelling gets into trouble.
2135             SwLineLayout& aRoot = rMulti.GetRoot();
2136             SwLinePortion* pPor = aRoot.GetFirstPortion();
2137             while ( pPor )
2138             {
2139                 if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() )
2140                 {
2141                     SwLinePortion* pHolePor = pPor->GetPortion();
2142                     pPor->SetPortion( NULL );
2143                     aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
2144                     rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
2145                     rMulti.SetPortion( pHolePor );
2146                     break;
2147                 }
2148                 pPor = pPor->GetPortion();
2149             }
2150 
2151             pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
2152                                     ((SwBidiPortion&)rMulti).GetLevel() );
2153 		}
2154         else
2155             pTmp = NULL;
2156 
2157         if ( ! rMulti.GetLen() && rInf.GetLast() )
2158         {
2159             SeekAndChgBefore( rInf );
2160             rInf.GetLast()->FormatEOL( rInf );
2161         }
2162 
2163         if( pNextFirst && pTmp )
2164         {
2165             pTmp->SetFollowFld();
2166             pTmp->GetRoot().SetPortion( pNextFirst );
2167         }
2168         else
2169             // A follow field portion is still waiting. If nobody wants it,
2170             // we delete it.
2171             delete pNextFirst;
2172 
2173         rInf.SetRest( pTmp );
2174     }
2175 
2176 	rInf.SetTxt( *pOldTxt );
2177     rInf.SetPaintOfst( nOldPaintOfst );
2178     rInf.SetStop( aInf.IsStop() );
2179     rInf.SetNumDone( sal_True );
2180     rInf.SetFtnDone( sal_True );
2181 	SeekAndChg( rInf );
2182 	delete pFirstRest;
2183 	delete pSecondRest;
2184 	delete pFontSave;
2185 	return bRet;
2186 }
2187 
2188 /*-----------------08.11.00 09:29-------------------
2189  * SwTxtFormatter::MakeRestPortion(..)
2190  * When a fieldportion at the end of line breaks and needs a following
2191  * fieldportion in the next line, then the "restportion" of the formatinfo
2192  * has to be set. Normally this happens during the formatting of the first
2193  * part of the fieldportion.
2194  * But sometimes the formatting starts at the line with the following part,
2195  * exspecally when the following part is on the next page.
2196  * In this case the MakeRestPortion-function has to create the following part.
2197  * The first parameter is the line that contains possibly a first part
2198  * of a field. When the function finds such field part, it creates the right
2199  * restportion. This may be a multiportion, e.g. if the field is surrounded by
2200  * a doubleline- or ruby-portion.
2201  * The second parameter is the start index of the line.
2202  * --------------------------------------------------*/
2203 
2204 SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine,
2205     xub_StrLen nPosition )
2206 {
2207     if( !nPosition )
2208 		return NULL;
2209     xub_StrLen nMultiPos = nPosition - pLine->GetLen();
2210 	const SwMultiPortion *pTmpMulti = NULL;
2211     const SwMultiPortion *pHelpMulti = NULL;
2212 	const SwLinePortion* pPor = pLine->GetFirstPortion();
2213 	SwFldPortion *pFld = NULL;
2214 	while( pPor )
2215 	{
2216 		if( pPor->GetLen() )
2217 		{
2218             if( !pHelpMulti )
2219 			{
2220 				nMultiPos = nMultiPos + pPor->GetLen();
2221 				pTmpMulti = NULL;
2222 			}
2223 		}
2224 		if( pPor->InFldGrp() )
2225 		{
2226             if( !pHelpMulti )
2227 				pTmpMulti = NULL;
2228 			pFld = (SwFldPortion*)pPor;
2229 		}
2230 		else if( pPor->IsMultiPortion() )
2231 		{
2232             ASSERT( !pHelpMulti || pHelpMulti->IsBidi(),
2233                     "Nested multiportions are forbidden." );
2234 
2235 			pFld = NULL;
2236 			pTmpMulti = (SwMultiPortion*)pPor;
2237 		}
2238 		pPor = pPor->GetPortion();
2239 		// If the last portion is a multi-portion, we enter it
2240 		// and look for a field portion inside.
2241 		// If we are already in a multiportion, we could change to the
2242 		// next line
2243 		if( !pPor && pTmpMulti )
2244 		{
2245             if( pHelpMulti )
2246 			{   // We're already inside the multiportion, let's take the second
2247 				// line, if we are in a double line portion
2248                 if( !pHelpMulti->IsRuby() )
2249                     pPor = pHelpMulti->GetRoot().GetNext();
2250 				pTmpMulti = NULL;
2251 			}
2252 			else
2253 			{   // Now we enter a multiportion, in a ruby portion we take the
2254 				// main line, not the phonetic line, in a doublelineportion we
2255 				// starts with the first line.
2256                 pHelpMulti = pTmpMulti;
2257                 nMultiPos = nMultiPos - pHelpMulti->GetLen();
2258                 if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
2259                     pPor = pHelpMulti->GetRoot().GetNext();
2260 				else
2261                     pPor = pHelpMulti->GetRoot().GetFirstPortion();
2262 			}
2263 		}
2264 	}
2265 	if( pFld && !pFld->HasFollow() )
2266 		pFld = NULL;
2267 
2268 	SwLinePortion *pRest = NULL;
2269 	if( pFld )
2270 	{
2271         const SwTxtAttr *pHint = GetAttr( nPosition - 1 );
2272 		if( pHint && pHint->Which() == RES_TXTATR_FIELD )
2273 		{
2274 			pRest = NewFldPortion( GetInfo(), pHint );
2275 			if( pRest->InFldGrp() )
2276 				((SwFldPortion*)pRest)->TakeNextOffset( pFld );
2277 			else
2278 			{
2279 				delete pRest;
2280 				pRest = NULL;
2281 			}
2282 		}
2283 	}
2284     if( !pHelpMulti )
2285 		return pRest;
2286 
2287     nPosition = nMultiPos + pHelpMulti->GetLen();
2288     SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 );
2289 
2290     if ( !pCreate )
2291     {
2292         ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" );
2293         if ( nMultiPos )
2294             --nMultiPos;
2295         pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 );
2296     }
2297 
2298     if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
2299         ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) )
2300 	{
2301 		SwMultiPortion* pTmp;
2302         if( pHelpMulti->IsDouble() )
2303 			pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
2304         else if( pHelpMulti->IsBidi() )
2305             pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
2306         else if( pHelpMulti->IsRuby() )
2307         {
2308             sal_Bool bRubyTop;
2309             sal_Bool* pRubyPos = 0;
2310 
2311             if ( GetInfo().SnapToGrid() )
2312             {
2313                 GETGRID( pFrm->FindPageFrm() )
2314                 if ( pGrid )
2315                 {
2316                     bRubyTop = ! pGrid->GetRubyTextBelow();
2317                     pRubyPos = &bRubyTop;
2318                 }
2319             }
2320 
2321             pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
2322                                       *pFrm->GetTxtNode()->getIDocumentSettingAccess(),
2323                                        nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(),
2324                                        pRubyPos );
2325         }
2326         else if( pHelpMulti->HasRotation() )
2327             pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
2328 		else
2329 		{
2330 			delete pCreate;
2331 			return pRest;
2332 		}
2333 		delete pCreate;
2334 		pTmp->SetFollowFld();
2335 		if( pRest )
2336 		{
2337 			SwLineLayout *pLay = &pTmp->GetRoot();
2338 			if( pTmp->IsRuby() && pTmp->OnTop() )
2339 			{
2340 				pLay->SetNext( new SwLineLayout() );
2341 				pLay = pLay->GetNext();
2342 			}
2343 			pLay->SetPortion( pRest );
2344 		}
2345 		return pTmp;
2346 	}
2347 	return pRest;
2348 }
2349 
2350 
2351 
2352 /*-----------------23.10.00 10:47-------------------
2353  * SwTxtCursorSave notes the start and current line of a SwTxtCursor,
2354  * sets them to the values for GetCrsrOfst inside a multiportion
2355  * and restores them in the destructor.
2356  * --------------------------------------------------*/
2357 
2358 SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor,
2359                                   SwMultiPortion* pMulti,
2360                                   SwTwips nY,
2361                                   sal_uInt16& nX,
2362                                   xub_StrLen nCurrStart,
2363                                   long nSpaceAdd )
2364 {
2365 	pTxtCrsr = pTxtCursor;
2366 	nStart = pTxtCursor->nStart;
2367 	pTxtCursor->nStart = nCurrStart;
2368 	pCurr = pTxtCursor->pCurr;
2369 	pTxtCursor->pCurr = &pMulti->GetRoot();
2370     while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY &&
2371 		pTxtCursor->Next() )
2372 		; // nothing
2373 	nWidth = pTxtCursor->pCurr->Width();
2374 	nOldProp = pTxtCursor->GetPropFont();
2375 
2376     if ( pMulti->IsDouble() || pMulti->IsBidi() )
2377     {
2378 		bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd );
2379 
2380         sal_uInt16 nSpaceCnt;
2381         if ( pMulti->IsDouble() )
2382         {
2383             pTxtCursor->SetPropFont( 50 );
2384             nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
2385         }
2386         else
2387         {
2388             const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx();
2389             pTxtCursor->GetInfo().SetIdx ( nCurrStart );
2390             nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo());
2391             pTxtCursor->GetInfo().SetIdx ( nOldIdx );
2392         }
2393 
2394 		if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
2395             pTxtCursor->pCurr->Width( static_cast<sal_uInt16>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) );
2396 
2397         // For a BidiPortion we have to calculate the offset from the
2398         // end of the portion
2399         if ( nX && pMulti->IsBidi() )
2400             nX = pTxtCursor->pCurr->Width() - nX;
2401     }
2402     else
2403 		bSpaceChg = sal_False;
2404 }
2405 
2406 SwTxtCursorSave::~SwTxtCursorSave()
2407 {
2408 	if( bSpaceChg )
2409 		SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr );
2410 	pTxtCrsr->pCurr->Width( KSHORT(nWidth) );
2411 	pTxtCrsr->pCurr = pCurr;
2412 	pTxtCrsr->nStart = nStart;
2413 	pTxtCrsr->SetPropFont( nOldProp );
2414 }
2415 
2416