/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"


#include <hintids.hxx>

#include <com/sun/star/i18n/ScriptType.hdl>
#include <vcl/graph.hxx>
#include <editeng/brshitem.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
#include <viewopt.hxx>	// SwViewOptions
#include <txtcfg.hxx>
#include <SwPortionHandler.hxx>
#include <porlay.hxx>
#include <porfld.hxx>
#include <inftxt.hxx>
#include <blink.hxx>	// pBlink
#include <frmtool.hxx>  // DrawGraphic
#include <viewsh.hxx>
#include <docsh.hxx>
#include <doc.hxx>
#include "rootfrm.hxx"
#include <breakit.hxx>
#include <porrst.hxx>
#include <porftn.hxx>   // SwFtnPortion
#include <accessibilityoptions.hxx>
#include <editeng/lrspitem.hxx>

#include <unicode/ubidi.h>

using namespace ::com::sun::star;

/*************************************************************************
 *                      class SwFldPortion
 *************************************************************************/

SwLinePortion *SwFldPortion::Compress()
{ return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; }

SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const
{
    SwFont *pNewFnt;
    if( 0 != ( pNewFnt = pFnt ) )
    {
        pNewFnt = new SwFont( *pFnt );
    }
    SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
    pClone->SetNextOffset( nNextOffset );
    pClone->m_bNoLength = this->m_bNoLength;
    return pClone;
}

void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
{
	ASSERT( pFld, "TakeNextOffset: Missing Source" );
	nNextOffset = pFld->GetNextOffset();
	aExpand.Erase( 0, nNextOffset );
	bFollow = sal_True;
}

SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
    : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
      bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
    , m_bNoLength( sal_False )
{
    SetWhichPor( POR_FLD );
	m_nAttrFldType = 0;
}

SwFldPortion::SwFldPortion( const SwFldPortion& rFld )
    : SwExpandPortion( rFld ),
      aExpand( rFld.GetExp() ),
      nNextOffset( rFld.GetNextOffset() ),
      nNextScriptChg( rFld.GetNextScriptChg() ),
      bFollow( rFld.IsFollow() ),
      bLeft( rFld.IsLeft() ),
      bHide( rFld.IsHide() ),
      bCenter( rFld.IsCenter() ),
      bHasFollow( rFld.HasFollow() ),
      bPlaceHolder( rFld.bPlaceHolder )
    , m_bNoLength( rFld.m_bNoLength )
{
    if ( rFld.HasFont() )
        pFnt = new SwFont( *rFld.GetFont() );
    else
        pFnt = 0;

    SetWhichPor( POR_FLD );
}

SwFldPortion::~SwFldPortion()
{
	delete pFnt;
	if( pBlink )
		pBlink->Delete( this );
}

/*************************************************************************
 *               virtual SwFldPortion::GetViewWidth()
 *************************************************************************/

KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
{
	// Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
	// Moment errechnet werden:
	SwFldPortion* pThis = (SwFldPortion*)this;
    if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
            !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
	{
		if( !nViewWidth )
			pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width();
	}
	else
		pThis->nViewWidth = 0;
	return nViewWidth;
}

/*************************************************************************
 *                 virtual SwFldPortion::Format()
 *************************************************************************/

// 8653: in keinem Fall nur SetLen(0);

/*************************************************************************
 *	 Hilfsklasse SwFldSlot
 **************************************************************************/

class SwFldSlot
{
	const XubString *pOldTxt;
	XubString aTxt;
	xub_StrLen nIdx;
	xub_StrLen nLen;
	sal_Bool bOn;
    SwTxtFormatInfo *pInf;
public:
    SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor );
	~SwFldSlot();
};

SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor )
{
	bOn = pPor->GetExpTxt( *pNew, aTxt );

	// Der Text wird ausgetauscht...
	if( bOn )
	{
        pInf = (SwTxtFormatInfo*)pNew;
		nIdx = pInf->GetIdx();
		nLen = pInf->GetLen();
		pOldTxt = &(pInf->GetTxt());
		pInf->SetLen( aTxt.Len() );
		if( pPor->IsFollow() )
        {
            pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
            pInf->SetIdx( 0 );
        }
		else
		{
			XubString aTmp( aTxt );
			aTxt = *pOldTxt;
			aTxt.Erase( nIdx, 1 );
			aTxt.Insert( aTmp, nIdx );
		}
		pInf->SetTxt( aTxt );
	}
}

SwFldSlot::~SwFldSlot()
{
	if( bOn )
	{
		pInf->SetTxt( *pOldTxt );
		pInf->SetIdx( nIdx );
		pInf->SetLen( nLen );
        pInf->SetFakeLineStart( sal_False );
	}
}

void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf )
{
	String aTxt;
    if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() )
	{
		sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
		sal_uInt16 nScript;
		{
			nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 );
			xub_StrLen nChg = 0;
            if( i18n::ScriptType::WEAK == nScript )
			{
				nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript);
				if( nChg < aTxt.Len() )
					nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg );
			}

            //
            // nNextScriptChg will be evaluated during SwFldPortion::Format()
            //
            if ( nChg < aTxt.Len() )
                nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript );
            else
                nNextScriptChg = aTxt.Len();

        }
		sal_uInt8 nTmp;
		switch ( nScript ) {
			case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
			case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
			case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
			default: nTmp = nActual;
		}

        // #i16354# Change script type for RTL text to CTL.
        const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
        // --> OD 2009-01-29 #i98418#
//        const sal_uInt8 nFldDir = IsNumberPortion() ?
        const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
                             rSI.GetDefaultDir() :
                             rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
        // <--
        if ( UBIDI_RTL == nFldDir )
        {
            UErrorCode nError = U_ZERO_ERROR;
            UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError );
            ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError );
            int32_t nEnd;
            UBiDiLevel nCurrDir;
            ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
            ubidi_close( pBidi );
            const xub_StrLen nNextDirChg = (xub_StrLen)nEnd;
            nNextScriptChg = Min( nNextScriptChg, nNextDirChg );

            // #i89825# change the script type also to CTL
            // if there is no strong LTR char in the LTR run (numbers)
            if ( nCurrDir != UBIDI_RTL )
            {
                nCurrDir = UBIDI_RTL;
                for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
                {
                    UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx ));
                    if ( nCharDir == U_LEFT_TO_RIGHT ||
                         nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
                         nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
                    {
                        nCurrDir = UBIDI_LTR;
                        break;
                    }
                }
            }

            if ( nCurrDir == UBIDI_RTL )
                nTmp = SW_CTL;
        }

        // --> OD 2009-01-29 #i98418#
        // keep determined script type for footnote portions as preferred script type.
        // For footnote portions a font can not be created directly - see footnote
        // portion format method.
//         if( !IsFtnPortion() && nTmp != nActual )
        if ( IsFtnPortion() )
        {
            dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp );
        }
        else if ( nTmp != nActual )
        {
    	    if( !pFnt )
                pFnt = new SwFont( *rInf.GetFont() );
            pFnt->SetActual( nTmp );
        }
        // <--
    }
}

sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf )
{
	// Scope wegen aDiffTxt::DTOR!
	xub_StrLen nRest;
	sal_Bool bFull;
	sal_Bool bEOL = sal_False;
	long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx();
	{
        SwFldSlot aDiffTxt( &rInf, this );
        SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
        aLayoutModeModifier.SetAuto();

        // Field portion has to be split in several parts if
        // 1. There are script/direction changes inside the field
        // 2. There are portion breaks (tab, break) inside the field:
		const xub_StrLen nOldFullLen = rInf.GetLen();
        xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
        if ( nNextScriptChg < nFullLen )
        {
            nFullLen = nNextScriptChg;
            rInf.SetHookChar( 0 );
        }
		rInf.SetLen( nFullLen );

        if ( STRING_LEN != rInf.GetUnderScorePos() &&
             rInf.GetUnderScorePos() > rInf.GetIdx() )
             rInf.SetUnderScorePos( rInf.GetIdx() );

		if( pFnt )
			pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );

		SwFontSave aSave( rInf, pFnt );

		// 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge
		// gesetzt und wird in nRest uebertragen. Ansonsten bleibt die
		// Laenge erhalten und wuerde auch in nRest einfliessen!
		SetLen(0);
   		const MSHORT nFollow = IsFollow() ? 0 : 1;

		// So komisch es aussieht, die Abfrage auf GetLen() muss wegen der
		// ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs)
		// sal_False returnen wegen SetFull ...
		if( !nFullLen )
		{
			// nicht Init(), weil wir Hoehe und Ascent brauchen
			Width(0);
			bFull = rInf.Width() <= rInf.GetPos().X();
		}
		else
		{
			xub_StrLen nOldLineStart = rInf.GetLineStart();
			if( IsFollow() )
				rInf.SetLineStart( 0 );
			rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow );

            // the height depending on the fields font is set,
            // this is required for SwTxtGuess::Guess
            Height( rInf.GetTxtHeight() );
            // If a kerning portion is inserted after our field portion,
            // the ascent and height must be known
            SetAscent( rInf.GetAscent() );
            bFull = SwTxtPortion::Format( rInf );
			rInf.SetNotEOL( sal_False );
			rInf.SetLineStart( nOldLineStart );
		}
		xub_StrLen nTmpLen = GetLen();
		bEOL = !nTmpLen && nFollow && bFull;
		nRest = nOldFullLen - nTmpLen;

        // Das Zeichen wird in der ersten Portion gehalten.
		// Unbedingt nach Format!
		SetLen( (m_bNoLength) ? 0 : nFollow );

        if( nRest )
		{
			// aExpand ist noch nicht gekuerzt worden, der neue Ofst
			// ergibt sich durch nRest.
            xub_StrLen nNextOfst = aExpand.Len() - nRest;

            if ( IsQuoVadisPortion() )
                nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len();

			XubString aNew( aExpand, nNextOfst, STRING_LEN );
			aExpand.Erase( nNextOfst, STRING_LEN );

			// These characters should not be contained in the follow
            // field portion. They are handled via the HookChar mechanism.
			switch( aNew.GetChar( 0 ))
			{
				case CH_BREAK  : bFull = sal_True;
							// kein break;
				case ' ' :
				case CH_TAB    :
                case CHAR_HARDHYPHEN:               // non-breaking hyphen
                case CHAR_SOFTHYPHEN:
                case CHAR_HARDBLANK:
                case CHAR_ZWSP :
                case CHAR_ZWNBSP :
                case CH_TXTATR_BREAKWORD:
                case CH_TXTATR_INWORD:                    
				{
					aNew.Erase( 0, 1 );
					++nNextOfst;
					break;
				}
                default: ;
			}

            // Even if there is no more text left for a follow field,
            // we have to build a follow field portion (without font),
            // otherwise the HookChar mechanism would not work.
            SwFldPortion *pFld = Clone( aNew );
			if( aNew.Len() && !pFld->GetFont() )
			{
				SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
				pFld->SetFont( pNewFnt );
			}
			pFld->SetFollow( sal_True );
			SetHasFollow( sal_True );
			// In nNextOffset steht bei einem neuangelegten Feld zunaechst
			// der Offset, an dem es selbst im Originalstring beginnt.
			// Wenn beim Formatieren ein FollowFeld angelegt wird, wird
			// der Offset dieses FollowFelds in nNextOffset festgehalten.
			nNextOffset = nNextOffset + nNextOfst;
			pFld->SetNextOffset( nNextOffset );
			rInf.SetRest( pFld );
		}
	}

	if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
		rInf.GetLast()->FormatEOL( rInf );
	return bFull;
}

/*************************************************************************
 *               virtual SwFldPortion::Paint()
 *************************************************************************/

void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	SwFontSave aSave( rInf, pFnt );

	ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
    if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
	{
		// Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
		rInf.DrawViewOpt( *this, POR_FLD );
		SwExpandPortion::Paint( rInf );
	}
}

/*************************************************************************
 *              virtual SwFldPortion::GetExpTxt()
 *************************************************************************/

sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
	rTxt = aExpand;
    if( !rTxt.Len() && rInf.OnWin() &&
        !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
            SwViewOption::IsFieldShadings() &&
            !HasFollow() )
		rTxt = ' ';
	return sal_True;
}

/*************************************************************************
 *              virtual SwFldPortion::HandlePortion()
 *************************************************************************/

void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
{
    rPH.Special( GetLen(), aExpand, GetWhichPor() );
    if( GetWhichPor() == POR_FLD )
    {
    	rPH.SetAttrFieldType(m_nAttrFldType);
    }
}

/*************************************************************************
 *                virtual SwFldPortion::GetTxtSize()
 *************************************************************************/

SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
	SwFontSave aSave( rInf, pFnt );
	SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
	return aSize;
}

/*************************************************************************
 *                      class SwHiddenPortion
 *************************************************************************/

SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
{
	SwFont *pNewFnt;
	if( 0 != ( pNewFnt = pFnt ) )
		pNewFnt = new SwFont( *pFnt );
	return new SwHiddenPortion( rExpand, pNewFnt );
}

/*************************************************************************
 *               virtual SwHiddenPortion::Paint()
 *************************************************************************/

void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	if( Width() )
	{
		SwFontSave aSave( rInf, pFnt );
		rInf.DrawViewOpt( *this, POR_HIDDEN );
		SwExpandPortion::Paint( rInf );
	}
}

/*************************************************************************
 *              virtual SwHiddenPortion::GetExpTxt()
 *************************************************************************/

sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
	// Nicht auf IsHidden() abfragen !
	return SwFldPortion::GetExpTxt( rInf, rTxt );
}

/*************************************************************************
 *                      class SwNumberPortion
 *************************************************************************/

// --> OD 2008-01-23 #newlistlevelattrs#
SwNumberPortion::SwNumberPortion( const XubString &rExpand,
                                  SwFont *pFont,
                                  const sal_Bool bLft,
                                  const sal_Bool bCntr,
                                  const KSHORT nMinDst,
                                  const bool bLabelAlignmentPosAndSpaceModeActive )
        : SwFldPortion( rExpand, pFont ),
          nFixWidth(0),
          nMinDist( nMinDst ),
          // --> OD 2008-01-23 #newlistlevelattrs#
          mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
          // <--
{
	SetWhichPor( POR_NUMBER );
	SetLeft( bLft );
	SetHide( sal_False );
	SetCenter( bCntr );
}

xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
{
	return 0;
}

SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
{
	SwFont *pNewFnt;
	if( 0 != ( pNewFnt = pFnt ) )
		pNewFnt = new SwFont( *pFnt );
    // --> OD 2008-01-23 #newlistlevelattrs#
	return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
                                nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
    // <--
}

/*************************************************************************
 *                 virtual SwNumberPortion::Format()
 *************************************************************************/

// 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
// 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
// eingibt, bis die Zeile ueberlaeuft.
// Man muss die Fly-Ausweichmanoever beachten!

sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
{
	SetHide( sal_False );
	const sal_Bool bFull = SwFldPortion::Format( rInf );
	SetLen( 0 );
    // a numbering portion can be contained in a rotated portion!!!
    nFixWidth = rInf.IsMulti() ? Height() : Width();
	rInf.SetNumDone( !rInf.GetRest() );
	if( rInf.IsNumDone() )
	{
//        SetAscent( rInf.GetAscent() );
        ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );

        long nDiff( 0 );
        // --> OD 2008-01-23 #newlistlevelattrs#
        if ( !mbLabelAlignmentPosAndSpaceModeActive )
        {
            if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
                 // --> FME 2004-08-13 #i32902#
                 !IsFtnNumPortion() )
                 // <--
            {
                nDiff = rInf.Left()
                    + rInf.GetTxtFrm()->GetTxtNode()->
                    GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
                    - rInf.First()
                    + rInf.ForcedLeftMargin();
            }
            else
            {
                nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
            }
        }
        // <--
        // Ein Vorschlag von Juergen und Volkmar:
        // Der Textteil hinter der Numerierung sollte immer
        // mindestens beim linken Rand beginnen.
        if( nDiff < 0 )
            nDiff = 0;
        else if ( nDiff > rInf.X() )
            nDiff -= rInf.X();
        else
            nDiff = 0;

		if( nDiff < nFixWidth + nMinDist )
			nDiff = nFixWidth + nMinDist;
		// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
		// fieser Sonderfall: FlyFrm liegt in dem Bereich,
		// den wir uns gerade unter den Nagel reissen wollen.
		// Die NumberPortion wird als verborgen markiert.
        const sal_Bool bFly = rInf.GetFly() ||
            ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
        if( nDiff > rInf.Width() )
		{
			nDiff = rInf.Width();
            if ( bFly )
                SetHide( sal_True );
		}

        // A numbering portion can be inside a SwRotatedPortion. Then the
        // Height has to be changed
        if ( rInf.IsMulti() )
        {
            if ( Height() < nDiff )
                Height( KSHORT( nDiff ) );
        }
        else if( Width() < nDiff )
            Width( KSHORT(nDiff) );
	}
	return bFull;
}

void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
{
/*	Ein FormatEOL deutet daraufhin, dass der folgende Text
 *	nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
 *  wird diese NumberPortion verborgen.
 */

    // This caused trouble with flys anchored as characters.
    // If one of these is numbered but does not fit to the line,
    // it calls this function, causing a loop because both the number
    // portion and the fly portion go to the next line
//    SetHide( sal_True );
}

/*************************************************************************
 *               virtual SwNumberPortion::Paint()
 *************************************************************************/

void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
/*	Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
 * 	Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
 */

	if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
	{
		SwLinePortion *pTmp = GetPortion();
		while ( pTmp && !pTmp->InTxtGrp() )
			pTmp = pTmp->GetPortion();
		if ( !pTmp )
			return;
	}

    // calculate the width of the number portion, including follows
    const KSHORT nOldWidth = Width();
    sal_uInt16 nSumWidth = 0;
    sal_uInt16 nOffset = 0;

    const SwLinePortion* pTmp = this;
    while ( pTmp && pTmp->InNumberGrp() )
    {
        nSumWidth = nSumWidth + pTmp->Width();
        if ( ((SwNumberPortion*)pTmp)->HasFollow() )
            pTmp = pTmp->GetPortion();
        else
        {
            nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
            break;
        }
    }

    // The master portion takes care for painting the background of the
    // follow field portions
    if ( ! IsFollow() )
    {
        SwLinePortion *pThis = (SwLinePortion*)this;
        pThis->Width( nSumWidth );
        rInf.DrawViewOpt( *this, POR_NUMBER );
        pThis->Width( nOldWidth );
    }

	if( aExpand.Len() )
	{
		const SwFont *pTmpFnt = rInf.GetFont();
		sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
							     UNDERLINE_NONE != pTmpFnt->GetOverline()  ||
							     STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
							     !pTmpFnt->IsWordLineMode();
		if( bPaintSpace && pFnt )
			bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
							UNDERLINE_NONE != pFnt->GetOverline()  ||
							STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
							!pFnt->IsWordLineMode();

		SwFontSave aSave( rInf, pFnt );

        if( nFixWidth == Width() && ! HasFollow() )
			SwExpandPortion::Paint( rInf );
		else
		{
			// logisches const: Width wird wieder zurueckgesetzt
			SwLinePortion *pThis = (SwLinePortion*)this;
			bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
			KSHORT nSpaceOffs = nFixWidth;
			pThis->Width( nFixWidth );

			if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
                ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
				SwExpandPortion::Paint( rInf );
			else
			{
				SwTxtPaintInfo aInf( rInf );
				if( nOffset < nMinDist )
					nOffset = 0;
				else
				{
					if( IsCenter() )
					{
                        /* #110778# a / 2 * 2 == a is not a tautology */
                        KSHORT nTmpOffset = nOffset;
						nOffset /= 2;
						if( nOffset < nMinDist )
							nOffset = nTmpOffset - nMinDist;
					}
					else
						nOffset = nOffset - nMinDist;
				}
				aInf.X( aInf.X() + nOffset );
				SwExpandPortion::Paint( aInf );
				if( bPaintSpace )
					nSpaceOffs = nSpaceOffs + nOffset;
			}
			if( bPaintSpace && nOldWidth > nSpaceOffs )
			{
				SwTxtPaintInfo aInf( rInf );
static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
				aInf.X( aInf.X() + nSpaceOffs );

                // --> FME 2005-08-12 #i53199# Adjust position of underline:
                if ( rInf.GetUnderFnt() )
                {
                    const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
                    rInf.GetUnderFnt()->SetPos( aNewPos );
                }
                // <--

				pThis->Width( nOldWidth - nSpaceOffs + 12 );
				{
                    SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
					aInf.DrawText( *this, aInf.GetLen(), sal_True );
				}
			}
			pThis->Width( nOldWidth );
		}
	}
}


/*************************************************************************
 *                      class SwBulletPortion
 *************************************************************************/

// --> OD 2008-01-23 #newlistlevelattrs#
SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
                                  const XubString& rBulletFollowedBy,
                                  SwFont *pFont,
                                  const sal_Bool bLft,
                                  const sal_Bool bCntr,
                                  const KSHORT nMinDst,
                                  const bool bLabelAlignmentPosAndSpaceModeActive )
    : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
                       pFont, bLft, bCntr, nMinDst,
                       bLabelAlignmentPosAndSpaceModeActive )
// <--
{
	SetWhichPor( POR_BULLET );
}

/*************************************************************************
 *                      class SwGrfNumPortion
 *************************************************************************/

#define GRFNUM_SECURE 10

// --> OD 2008-01-23 #newlistlevelattrs#
SwGrfNumPortion::SwGrfNumPortion(
		SwFrm*,
        const XubString& rGraphicFollowedBy,
		const SvxBrushItem* pGrfBrush,
		const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
        const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
        const bool bLabelAlignmentPosAndSpaceModeActive ) :
    SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
                     bLabelAlignmentPosAndSpaceModeActive ),
// <--
    pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
{
	SetWhichPor( POR_GRFNUM );
	SetAnimated( sal_False );
	bReplace = sal_False;
	if( pGrfBrush )
	{
		*pBrush = *pGrfBrush;
		const Graphic* pGraph = pGrfBrush->GetGraphic();
		if( pGraph )
			SetAnimated( pGraph->IsAnimated() );
		else
			bReplace = sal_True;
	}
	if( pGrfOrient )
	{
		nYPos = pGrfOrient->GetPos();
		eOrient = pGrfOrient->GetVertOrient();
	}
	else
	{
		nYPos = 0;
        eOrient = text::VertOrientation::TOP;
	}
    Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
	nFixWidth = Width();
	nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
	Height( KSHORT(nGrfHeight) );
	bNoPaint = sal_False;
}

SwGrfNumPortion::~SwGrfNumPortion()
{
	if ( IsAnimated() )
		( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
	delete pBrush;
}

void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
{
	if ( IsAnimated() )
		( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
}

sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
{
	SetHide( sal_False );
    // --> OD 2008-01-29 #newlistlevelattrs#
//    Width( nFixWidth );
    KSHORT nFollowedByWidth( 0 );
    if ( mbLabelAlignmentPosAndSpaceModeActive )
    {
        SwFldPortion::Format( rInf );
        nFollowedByWidth = Width();
        SetLen( 0 );
    }
    Width( nFixWidth + nFollowedByWidth );
    // <--
	const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
	const sal_Bool bFly = rInf.GetFly() ||
		( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
    SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
	if( GetAscent() > Height() )
		Height( GetAscent() );

	if( bFull )
	{
		Width( rInf.Width() - (KSHORT)rInf.X() );
		if( bFly )
		{
			SetLen( 0 );
			SetNoPaint( sal_True );
			rInf.SetNumDone( sal_False );
			return sal_True;
		}
	}
	rInf.SetNumDone( sal_True );
    // --> OD 2008-01-23 #newlistlevelattrs#
//    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
    long nDiff = mbLabelAlignmentPosAndSpaceModeActive
                 ? 0
                 : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
    // <--
	// Ein Vorschlag von Juergen und Volkmar:
	// Der Textteil hinter der Numerierung sollte immer
	// mindestens beim linken Rand beginnen.
	if( nDiff < 0 )
		nDiff = 0;
	else if ( nDiff > rInf.X() )
		nDiff -= rInf.X();
	if( nDiff < nFixWidth + nMinDist )
		nDiff = nFixWidth + nMinDist;
	// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
	// fieser Sonderfall: FlyFrm liegt in dem Bereich,
	// den wir uns gerade unter den Nagel reissen wollen.
	// Die NumberPortion wird als verborgen markiert.
	if( nDiff > rInf.Width() )
	{
		nDiff = rInf.Width();
		if( bFly )
			SetHide( sal_True );
	}

	if( Width() < nDiff )
		Width( KSHORT(nDiff) );
	return bFull;
}

void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	if( DontPaint() )
		return;
/*	Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
 * 	Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
 */
	if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
	{
		SwLinePortion *pTmp = GetPortion();
		while ( pTmp && !pTmp->InTxtGrp() )
			pTmp = pTmp->GetPortion();
		if ( !pTmp )
			return;
	}
	Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
	long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
	Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );

    // --> OD 2008-02-05 #newlistlevelattrs#
    const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
                              ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
                              ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
    // <--

    if( nFixWidth < Width() && !bTmpLeft )
	{
		KSHORT nOffset = Width() - nFixWidth;
		if( nOffset < nMinDist )
			nOffset = 0;
		else
		{
			if( IsCenter() )
			{
				nOffset /= 2;
				if( nOffset < nMinDist )
					nOffset = Width() - nFixWidth - nMinDist;
			}
			else
				nOffset = nOffset - nMinDist;
		}
		aPos.X() += nOffset;
	}

	if( bReplace )
	{
		KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
		aSize = Size( nTmpH, nTmpH );
		aPos.Y() = rInf.Y() - nTmpH;
	}
	SwRect aTmp( aPos, aSize );

	sal_Bool bDraw = sal_True;

	if ( IsAnimated() )
	{
		bDraw = !rInf.GetOpt().IsGraphic();
		if( !nId )
		{
			SetId( long( rInf.GetTxtFrm() ) );
			rInf.GetTxtFrm()->SetAnimation();
		}
		if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
		{
			rInf.NoteAnimation();
            const ViewShell* pViewShell = rInf.GetVsh();

            // virtual device, not pdf export
            if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
                pViewShell && pViewShell->GetWin()  )
            {
				( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
				rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
			}


            else if ( pViewShell &&
                     !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
                     !pViewShell->IsPreView() &&
                      // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
                      pViewShell->GetWin() )
                      // <--
            {
				( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
					(OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
            }

            // pdf export, printing, preview, stop animations...
            else
                bDraw = sal_True;
		}
		if( bDraw )
			( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
	}

    SwRect aRepaint( rInf.GetPaintRect() );
	const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
    if( rFrm.IsVertical() )
    {
        rFrm.SwitchHorizontalToVertical( aTmp );
        rFrm.SwitchHorizontalToVertical( aRepaint );
    }

    if( rFrm.IsRightToLeft() )
    {
        rFrm.SwitchLTRtoRTL( aTmp );
        rFrm.SwitchLTRtoRTL( aRepaint );
    }

	if( bDraw && aTmp.HasArea() )
		DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
            aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
}

void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
							   long nFlyAsc, long nFlyDesc )
{
    if ( GetOrient() != text::VertOrientation::NONE )
	{
		SetRelPos( 0 );
        if ( GetOrient() == text::VertOrientation::CENTER )
			SetRelPos( GetGrfHeight() / 2 );
        else if ( GetOrient() == text::VertOrientation::TOP )
			SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
        else if ( GetOrient() == text::VertOrientation::BOTTOM )
			;
        else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
			SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
        else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
			SetRelPos( nLnAscent );
        else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
			SetRelPos( GetGrfHeight() - nLnDescent );
		else
		{
			if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
			{
				// wenn ich genauso gross bin wie die Zeile, brauche ich mich
				// nicht an der Zeile nicht weiter ausrichten, ich lasse
				// dann auch den max. Ascent der Zeile unveraendert

				SetRelPos( nFlyAsc );
			}
            else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
				SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
            else if ( GetOrient() == text::VertOrientation::LINE_TOP )
				SetRelPos( nFlyAsc );
            else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
				SetRelPos( GetGrfHeight() - nFlyDesc );
		}
	}
}

void SwTxtFrm::StopAnimation( OutputDevice* pOut )
{
	ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
	if( HasPara() )
	{
		SwLineLayout *pLine = GetPara();
		while( pLine )
		{
			SwLinePortion *pPor = pLine->GetPortion();
			while( pPor )
			{
				if( pPor->IsGrfNumPortion() )
					((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
				// Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
				// deshalb koennen wir abbrechen, sobald wir eine Portion mit
				// einer Laenge > 0 erreicht haben.
				pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
			}
			pLine = pLine->GetLen() ? 0 : pLine->GetNext();
		}
	}
}

/*************************************************************************
 * SwCombinedPortion::SwCombinedPortion(..)
 * initializes the script array and clears the width array
 *************************************************************************/

SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
	 : SwFldPortion( rTxt )
{
	SetLen(1);
	SetWhichPor( POR_COMBINED );
	if( aExpand.Len() > 6 )
		aExpand.Erase( 6 );
	// Initialization of the scripttype array,
	// the arrays of width and position are filled by the format function
	if(	pBreakIt->GetBreakIter().is() )
	{
		sal_uInt8 nScr = SW_SCRIPTS;
		for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
		{
			sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
			switch ( nScript ) {
				case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
				case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
				case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
			}
			aScrType[i] = nScr;
		}
	}
	else
	{
		for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
			; // nothing
	}
	memset( &aWidth, 0, sizeof(aWidth) );
}

/*************************************************************************
 * SwCombinedPortion::Paint(..)
 *************************************************************************/

void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
	if( Width() )
	{
		rInf.DrawBackBrush( *this );
		rInf.DrawViewOpt( *this, POR_FLD );

        // do we have to repaint a post it portion?
        if( rInf.OnWin() && pPortion && !pPortion->Width() )
            pPortion->PrePaint( rInf, this );

		sal_uInt16 nCount = aExpand.Len();
		if( !nCount )
			return;
		ASSERT( nCount < 7, "Too much combined characters" );

		// the first character of the second row
		sal_uInt16 nTop = ( nCount + 1 ) / 2;

		SwFont aTmpFont( *rInf.GetFont() );
		aTmpFont.SetProportion( nProportion );	// a smaller font
		SwFontSave aFontSave( rInf, &aTmpFont );

		sal_uInt16 i = 0;
		Point aOldPos = rInf.GetPos();
		Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
		while( i < nCount )
		{
			if( i == nTop ) // change the row
				aOutPos.Y() = aOldPos.Y() + nLowPos;	// Y of the second row
			aOutPos.X() = aOldPos.X() + aPos[i];		// X position
			const sal_uInt8 nAct = aScrType[i];				// script type
			aTmpFont.SetActual( nAct );
			// if there're more than 4 characters to display, we choose fonts
			// with 2/3 of the original font width.
			if( aWidth[ nAct ] )
			{
				Size aTmpSz = aTmpFont.GetSize( nAct );
				if( aTmpSz.Width() != aWidth[ nAct ] )
				{
					aTmpSz.Width() = aWidth[ nAct ];
					aTmpFont.SetSize( aTmpSz, nAct );
				}
			}
			((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
			rInf.DrawText( aExpand, *this, i, 1 );
			++i;
		}
		// rInf is const, so we have to take back our manipulations
		((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
	}
}

/*************************************************************************
 * SwCombinedPortion::Format(..)
 *************************************************************************/

sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
{
	sal_uInt16 nCount = aExpand.Len();
	if( !nCount )
	{
		Width( 0 );
		return sal_False;
	}

	ASSERT( nCount < 7, "Too much combined characters" );
	// If there are leading "weak"-scripttyped characters in this portion,
	// they get the actual scripttype.
	sal_uInt16 i = 0;
	while( i < nCount && SW_SCRIPTS == aScrType[i] )
		aScrType[i++] = rInf.GetFont()->GetActual();
	if( nCount > 4 )
	{
		// more than four? Ok, then we need the 2/3 font width
		i = 0;
		while( i < aExpand.Len() )
		{
			ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
			if( !aWidth[ aScrType[i] ] )
			{
				rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
                aWidth[ aScrType[i] ] =
                        static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
			}
			++i;
		}
	}

	sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
	ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
	SwFont aTmpFont( *rInf.GetFont() );
	SwFontSave aFontSave( rInf, &aTmpFont );
	nProportion = 55;
	// In nMainAscent/Descent we store the ascent and descent
	// of the original surrounding font
	sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
    sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
    const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
	nMainDescent = nMainDescent - nMainAscent;
	// we start with a 50% font, but if we notice that the combined portion
	// becomes bigger than the surrounding font, we check 45% and maybe 40%.
	do
	{
		nProportion -= 5;
		aTmpFont.SetProportion( nProportion );
		i = 0;
		memset( &aPos, 0, sizeof(aPos) );
		nMaxDescent = 0;
		nMaxAscent = 0;
		nMaxWidth = 0;
		nUpPos = nLowPos = 0;

		// Now we get the width of all characters.
		// The ascent and the width of the first line are stored in the
		// ascent member of the portion, the descent in nLowPos.
		// The ascent, descent and width of the second line are stored in the
		// local nMaxAscent, nMaxDescent and nMaxWidth variables.
		while( i < nCount )
		{
			sal_uInt8 nScrp = aScrType[i];
			aTmpFont.SetActual( nScrp );
			if( aWidth[ nScrp ] )
			{
				Size aFontSize( aTmpFont.GetSize( nScrp ) );
				aFontSize.Width() = aWidth[ nScrp ];
				aTmpFont.SetSize( aFontSize, nScrp );
			}

            SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
            Size aSize = aTmpFont._GetTxtSize( aDrawInf );
            sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
            aPos[ i ] = (sal_uInt16)aSize.Width();
			if( i == nTop ) // enter the second line
			{
				nLowPos = nMaxDescent;
				Height( nMaxDescent + nMaxAscent );
				Width( nMaxWidth );
				SetAscent( nMaxAscent );
				nMaxAscent = 0;
				nMaxDescent = 0;
				nMaxWidth = 0;
			}
			nMaxWidth = nMaxWidth + aPos[ i++ ];
			if( nAsc > nMaxAscent )
				nMaxAscent = nAsc;
			if( aSize.Height() - nAsc > nMaxDescent )
                nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
		}
		// for one or two characters we double the width of the portion
		if( nCount < 3 )
		{
			nMaxWidth *= 2;
			Width( 2*Width() );
			if( nCount < 2 )
			{
				Height( nMaxAscent + nMaxDescent );
				nLowPos = nMaxDescent;
			}
		}
		Height( Height() + nMaxDescent + nMaxAscent );
		nUpPos = nMaxAscent;
		SetAscent( Height() - nMaxDescent - nLowPos );
	} while( nProportion > 40 && ( GetAscent() > nMainAscent ||
									Height() - GetAscent() > nMainDescent ) );
	// if the combined portion is smaller than the surrounding text,
	// the portion grows. This looks better, if there's a character background.
	if( GetAscent() < nMainAscent )
	{
		Height( Height() + nMainAscent - GetAscent() );
		SetAscent( nMainAscent );
	}
	if( Height() < nMainAscent + nMainDescent )
		Height( nMainAscent + nMainDescent );

	// We calculate the x positions of the characters in both lines..
	sal_uInt16 nTopDiff = 0;
	sal_uInt16 nBotDiff = 0;
	if( nMaxWidth > Width() )
	{
		nTopDiff = ( nMaxWidth - Width() ) / 2;
		Width( nMaxWidth );
	}
	else
		nBotDiff = ( Width() - nMaxWidth ) / 2;
	switch( nTop)
	{
		case 3: aPos[1] = aPos[0] + nTopDiff;  // no break
		case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
	}
	aPos[0] = 0;
	switch( nCount )
	{
		case 5: aPos[4] = aPos[3] + nBotDiff;	// no break
		case 3: aPos[nTop] = nBotDiff;			break;
		case 6: aPos[4] = aPos[3] + nBotDiff;	// no break
		case 4: aPos[nTop] = 0;					// no break
		case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
	}

	// Does the combined portion fit the line?
	const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
	if( bFull )
	{
		if( rInf.GetLineStart() == rInf.GetIdx() &&	(!rInf.GetLast()->InFldGrp()
			|| !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
            Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
		else
		{
			Truncate();
			Width( 0 );
			SetLen( 0 );
			if( rInf.GetLast() )
				rInf.GetLast()->FormatEOL( rInf );
		}
	}
	return bFull;
}

/*************************************************************************
 * SwCombinedPortion::GetViewWidth(..)
 *************************************************************************/

KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
{
	if( !GetLen() )	// for the dummy part at the end of the line, where
		return 0;	// the combined portion doesn't fit.
	return SwFldPortion::GetViewWidth( rInf );
}
