LCOV - code coverage report
Current view: top level - sw/source/core/text - pormulti.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 841 1211 69.4 %
Date: 2015-06-13 12:38:46 Functions: 31 44 70.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11