xref: /trunk/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