/**************************************************************
 * 
 * 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 "flyfrm.hxx"	  // SwFlyInCntFrm
#include "viewopt.hxx"	// SwViewOptions
#include "errhdl.hxx"
#include "txtatr.hxx"  // SwINetFmt
#include <tools/multisel.hxx>
#include <editeng/escpitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/lrspitem.hxx>
#include <txtinet.hxx>
#include <fchrfmt.hxx>
#include <frmatr.hxx>
#include <sfx2/printer.hxx>
#include <fmtftn.hxx>
#include <fmtfld.hxx>
#include <fldbas.hxx>      // SwField
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <tgrditem.hxx>

// --> FME 2004-06-08 #i12836# enhanced pdf export
#include <EnhancedPDFExportHelper.hxx>
// <--


#include "flyfrms.hxx"
#include "viewsh.hxx"
#include "txtcfg.hxx"
#include "itrpaint.hxx"
#include "txtfrm.hxx"   // pFrm
#include "txtfly.hxx"
#include "swfont.hxx"
#include "txtpaint.hxx"
#include "portab.hxx"   // SwTabPortion::IsFilled
#include "porfly.hxx"	  // SwFlyCntPortion
#include "porfld.hxx"	// SwGrfNumPortion
#include "frmfmt.hxx"	// LRSpace
#include "txatbase.hxx" // SwTxtAttr
#include "charfmt.hxx"  // SwFmtCharFmt
#include "redlnitr.hxx" // SwRedlineItr
#include "porrst.hxx"	// SwArrowPortion
#include "pormulti.hxx"

/*************************************************************************
 *                  IsUnderlineBreak
 *
 * Returns, if we have an underline breaking situation
 * Adding some more conditions here means you also have to change them
 * in SwTxtPainter::CheckSpecialUnderline
 *************************************************************************/
sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
{
    return UNDERLINE_NONE == rFnt.GetUnderline() ||
           rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
           rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
           rPor.IsHolePortion() ||
          ( rPor.IsMultiPortion() && ! ((SwMultiPortion&)rPor).IsBidi() ) ||
           rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
           SVX_CASEMAP_KAPITAELCHEN == rFnt.GetCaseMap();
}

/*************************************************************************
 *					SwTxtPainter::CtorInitTxtPainter()
 *************************************************************************/
void SwTxtPainter::CtorInitTxtPainter( SwTxtFrm *pNewFrm, SwTxtPaintInfo *pNewInf )
{
    CtorInitTxtCursor( pNewFrm, pNewInf );
	pInf = pNewInf;
    SwFont *pMyFnt = GetFnt();
    GetInfo().SetFont( pMyFnt );
#ifdef DBG_UTIL
    if( ALIGN_BASELINE != pMyFnt->GetAlign() )
	{
        ASSERT( ALIGN_BASELINE == pMyFnt->GetAlign(),
				"+SwTxtPainter::CTOR: font alignment revolution" );
        pMyFnt->SetAlign( ALIGN_BASELINE );
	}
#endif
	bPaintDrop = sal_False;
}


/*************************************************************************
 *					  SwTxtPainter::CalcPaintOfst()
 *************************************************************************/
SwLinePortion *SwTxtPainter::CalcPaintOfst( const SwRect &rPaint )
{
	SwLinePortion *pPor = pCurr->GetFirstPortion();
	GetInfo().SetPaintOfst( 0 );
	SwTwips nPaintOfst = rPaint.Left();

	// nPaintOfst wurde exakt auf das Ende eingestellt, deswegen <=
	// nPaintOfst ist dokumentglobal, deswegen nLeftMar aufaddieren
	// const KSHORT nLeftMar = KSHORT(GetLeftMargin());
	// 8310: painten von LineBreaks in leeren Zeilen.
	if( nPaintOfst && pCurr->Width() )
	{
		SwLinePortion *pLast = 0;
		// 7529 und 4757: nicht <= nPaintOfst
		while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2)
					   < nPaintOfst )
		{
			DBG_LOOP;
			if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() )
			{
				long nTmp = GetInfo().X() +pPor->Width() +
					pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
				if( nTmp + (pPor->Height()/2) >= nPaintOfst )
					break;
				GetInfo().X( nTmp );
				GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() );
			}
			else
				pPor->Move( GetInfo() );
			pLast = pPor;
			pPor = pPor->GetPortion();
		}

		// 7529: bei PostIts auch pLast returnen.
		if( pLast && !pLast->Width() &&	pLast->IsPostItsPortion() )
		{
			pPor = pLast;
			GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() );
		}
	}
	return pPor;
}

/*************************************************************************
 *                    SwTxtPainter::DrawTextLine()
 *
 * Es gibt zwei Moeglichkeiten bei transparenten Font auszugeben:
 * 1) DrawRect auf die ganze Zeile und die DrawText hinterher
 *	  (objektiv schnell, subjektiv langsam).
 * 2) Fuer jede Portion ein DrawRect mit anschliessendem DrawText
 *	  ausgefuehrt (objektiv langsam, subjektiv schnell).
 * Da der User in der Regel subjektiv urteilt, wird die 2. Methode
 * als Default eingestellt.
 *************************************************************************/
void SwTxtPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
								 const sal_Bool bUnderSz )
{
#if OSL_DEBUG_LEVEL > 1
//    sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
//    sal_uInt16 nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() );
#endif

    // Adjustierung ggf. nachholen
	GetAdjusted();
    GetInfo().SetpSpaceAdd( pCurr->GetpLLSpaceAdd() );
	GetInfo().ResetSpaceIdx();
    GetInfo().SetKanaComp( pCurr->GetpKanaComp() );
    GetInfo().ResetKanaIdx();
	// Die Groesse des Frames
	GetInfo().SetIdx( GetStart() );
	GetInfo().SetPos( GetTopLeft() );

	const sal_Bool bDrawInWindow = GetInfo().OnWin();

	// 6882: Leerzeilen duerfen nicht wegoptimiert werden bei Paragraphzeichen.
	const sal_Bool bEndPor = GetInfo().GetOpt().IsParagraph() && !GetInfo().GetTxt().Len();

	SwLinePortion *pPor = bEndPor ? pCurr->GetFirstPortion() : CalcPaintOfst( rPaint );

	// Optimierung!
	const SwTwips nMaxRight = Min( rPaint.Right(), Right() );
	const SwTwips nTmpLeft = GetInfo().X();
	if( !bEndPor && nTmpLeft >= nMaxRight )
		return;

	// DropCaps!
	// 7538: natuerlich auch auf dem Drucker
	if( !bPaintDrop )
	{
		// 8084: Optimierung, weniger Painten.
		// AMA: Durch 8084 wurde 7538 wiederbelebt!
		// bDrawInWindow entfernt, damit DropCaps auch gedruckt werden
		bPaintDrop = pPor == pCurr->GetFirstPortion()
					 && GetDropLines() >= GetLineNr();
	}

	KSHORT nTmpHeight, nTmpAscent;
	CalcAscentAndHeight( nTmpAscent, nTmpHeight );

	// bClip entscheidet darueber, ob geclippt werden muss.
	// Das Ganze muss vor der Retusche stehen

	sal_Bool bClip = ( bDrawInWindow || bUnderSz ) && !rClip.IsChg();
	if( bClip && pPor )
	{
		// Wenn TopLeft oder BottomLeft der Line ausserhalb liegen,
		// muss geclippt werden. Die Ueberpruefung auf Right() erfolgt
		// in der folgenden Ausgabeschleife...

		if( GetInfo().GetPos().X() < rPaint.Left() ||
			GetInfo().GetPos().Y() < rPaint.Top() ||
			GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
		{
			bClip = sal_False;
            rClip.ChgClip( rPaint, pFrm, pCurr->HasUnderscore() );
		}
#if OSL_DEBUG_LEVEL > 1
		static sal_Bool bClipAlways = sal_False;
		if( bClip && bClipAlways )
		{	bClip = sal_False;
			rClip.ChgClip( rPaint );
		}
#endif
	}

	// Alignment:
	sal_Bool bPlus = sal_False;
    OutputDevice* pOut = GetInfo().GetOut();
	Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() );
	if ( aPnt1.X() < rPaint.Left() )
		aPnt1.X() = rPaint.Left();
	if ( aPnt1.Y() < rPaint.Top() )
		aPnt1.Y() = rPaint.Top();
	Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
				 GetInfo().GetPos().Y() + nTmpHeight );
	if ( aPnt2.X() > rPaint.Right() )
		aPnt2.X() = rPaint.Right();
	if ( aPnt2.Y() > rPaint.Bottom() )
	{
		aPnt2.Y() = rPaint.Bottom();
		bPlus = sal_True;
	}

	const SwRect aLineRect( aPnt1, aPnt2 );

	if( pCurr->IsClipping() )
	{
        rClip.ChgClip( aLineRect, pFrm );
		bClip = sal_False;
	}

	if( !pPor && !bEndPor )
	{
#ifdef DBGTXT
		aDbstream << "PAINTER: done nothing" << endl;
#endif
		return;
	}

	// Baseline-Ausgabe auch bei nicht-TxtPortions (vgl. TabPor mit Fill)
    // if no special vertical alignment is used,
    // we calculate Y value for the whole line
    GETGRID( GetTxtFrm()->FindPageFrm() )
    const sal_Bool bAdjustBaseLine =
        GetLineInfo().HasSpecialAlign( GetTxtFrm()->IsVertical() ) ||
        ( 0 != pGrid );
    const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
    if ( ! bAdjustBaseLine )
        GetInfo().Y( nLineBaseLine );

	// 7529: PostIts prepainten
	if( GetInfo().OnWin() && pPor && !pPor->Width() )
	{
		SeekAndChg( GetInfo() );

        if( bAdjustBaseLine )
        {
            const SwTwips nOldY = GetInfo().Y();

            GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *pCurr, 0,
                GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ),
                GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut )
            ) );

            pPor->PrePaint( GetInfo(), pPor );
            GetInfo().Y( nOldY );
        }
        else
            pPor->PrePaint( GetInfo(), pPor );
	}

	// 7923: EndPortions geben auch Zeichen aus, deswegen den Fnt wechseln!
	if( bEndPor )
		SeekStartAndChg( GetInfo() );

	sal_Bool bRest = pCurr->IsRest();
	sal_Bool bFirst = sal_True;

	SwArrowPortion *pArrow = NULL;
    // Reference portion for the paragraph end portion
    SwLinePortion* pEndTempl = pCurr->GetFirstPortion();

	while( pPor )
	{
		DBG_LOOP;
		sal_Bool bSeeked = sal_True;
		GetInfo().SetLen( pPor->GetLen() );

        const SwTwips nOldY = GetInfo().Y();

        if ( bAdjustBaseLine )
        {
            GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *pCurr, pPor ) );

            // we store the last portion, because a possible paragraph
            // end character has the same font as this portion
            // (only in special vertical alignment case, otherwise the first
            // portion of the line is used)
            if ( pPor->Width() && pPor->InTxtGrp() )
                pEndTempl = pPor;
        }

		// Ein Sonderfall sind GluePortions, die Blanks ausgeben.

		// 6168: Der Rest einer FldPortion zog sich die Attribute der naechsten
		// Portion an, dies wird durch SeekAndChgBefore vermieden:
		if( ( bRest && pPor->InFldGrp() && !pPor->GetLen() ) )
			SeekAndChgBefore( GetInfo() );
		else if ( pPor->IsQuoVadisPortion() )
		{
			xub_StrLen nOffset = GetInfo().GetIdx();
			SeekStartAndChg( GetInfo(), sal_True );
			if( GetRedln() && pCurr->HasRedline() )
				GetRedln()->Seek( *pFnt, nOffset, 0 );
		}
		else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() )
			SeekAndChg( GetInfo() );
		else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
		{
			// Paragraphzeichen sollten den gleichen Font wie das Zeichen vor
			// haben, es sei denn, es gibt Redlining in dem Absatz.
			if( GetRedln() )
				SeekAndChg( GetInfo() );
			else
				SeekAndChgBefore( GetInfo() );
		}
		else
			bSeeked = sal_False;

//		bRest = sal_False;

		// Wenn das Ende der Portion hinausragt, wird geclippt.
		// Es wird ein Sicherheitsabstand von Height-Halbe aufaddiert,
		// damit die TTF-"f" nicht im Seitenrand haengen...
        if( bClip &&
            GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
        {
            bClip = sal_False;
            rClip.ChgClip( rPaint, pFrm, pCurr->HasUnderscore() );
		}

		// Portions, die "unter" dem Text liegen wie PostIts
		SwLinePortion *pNext = pPor->GetPortion();
        if( GetInfo().OnWin() && pNext && !pNext->Width() )
		{
			// Fix 11289: Felder waren hier ausgeklammert wg. Last!=Owner beim
			// Laden von Brief.sdw. Jetzt sind die Felder wieder zugelassen,
			// durch bSeeked wird Last!=Owner vermieden.
			if ( !bSeeked )
				SeekAndChg( GetInfo() );
            pNext->PrePaint( GetInfo(), pPor );
		}

        // We calculate a separate font for underlining.
        CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
        SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
        if ( pUnderLineFnt )
        {
            const Point aTmpPoint( GetInfo().X(),
                                   bAdjustBaseLine ?
                                   pUnderLineFnt->GetPos().Y() :
                                   nLineBaseLine );
            pUnderLineFnt->SetPos( aTmpPoint );
        }


        // in extended input mode we do not want a common underline font.
        SwUnderlineFont* pOldUnderLineFnt = 0;
        if ( GetRedln() && GetRedln()->ExtOn() )
        {
            pOldUnderLineFnt = GetInfo().GetUnderFnt();
            GetInfo().SetUnderFnt( 0 );
        }

        {
            // --> FME 2004-06-24 #i16816# tagged pdf support
            Por_Info aPorInfo( *pPor, *this );
            SwTaggedPDFHelper aTaggedPDFHelper( 0, 0, &aPorInfo, *pOut );
            // <--

            if( pPor->IsMultiPortion() )
                PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor );
            else
                pPor->Paint( GetInfo() );
        }

        // reset underline font
        if ( pOldUnderLineFnt )
            GetInfo().SetUnderFnt( pOldUnderLineFnt );

        // reset (for special vertical alignment)
        GetInfo().Y( nOldY );

        if( GetFnt()->IsURL() && pPor->InTxtGrp() )
			GetInfo().NotifyURL( *pPor );

		bFirst &= !pPor->GetLen();
		if( pNext || !pPor->IsMarginPortion() )
			pPor->Move( GetInfo() );
		if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow )
			pArrow = (SwArrowPortion*)pPor;

        pPor = bDrawInWindow || GetInfo().X() <= nMaxRight ||
               // --> FME 2004-06-24 #i16816# tagged pdf support
               ( GetInfo().GetVsh() &&
                 GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
                 pNext && pNext->IsHolePortion() ) ?
               // <--
               pNext :
               0;
	}

    // delete underline font
    delete GetInfo().GetUnderFnt();
    GetInfo().SetUnderFnt( 0 );

    // paint remaining stuff
	if( bDrawInWindow )
	{
        // If special vertical alignment is enabled, GetInfo().Y() is the
        // top of the current line. Therefore is has to be adjusted for
        // the painting of the remaining stuff. We first store the old value.
        const SwTwips nOldY = GetInfo().Y();

        if( !GetNextLine() &&
			GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreView() &&
			GetInfo().GetOpt().IsParagraph() &&	!GetTxtFrm()->GetFollow() &&
			GetInfo().GetIdx() >= GetInfo().GetTxt().Len() )
		{
            const SwTmpEndPortion aEnd( *pEndTempl );
            GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut );

            if ( bAdjustBaseLine )
                GetInfo().Y( GetInfo().GetPos().Y()
                           + AdjustBaseLine( *pCurr, &aEnd ) );

            aEnd.Paint( GetInfo() );
            GetInfo().Y( nOldY );
        }
        if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreView() )
        {
            const sal_Bool bNextUndersized =
                ( GetTxtFrm()->GetNext() &&
                  0 == GetTxtFrm()->GetNext()->Prt().Height() &&
                  GetTxtFrm()->GetNext()->IsTxtFrm() &&
                  ((SwTxtFrm*)GetTxtFrm()->GetNext())->IsUndersized() ) ;

            if( bUnderSz || bNextUndersized )
			{
                if ( bAdjustBaseLine )
                    GetInfo().Y( GetInfo().GetPos().Y() + pCurr->GetAscent() );

                if( pArrow )
                    GetInfo().DrawRedArrow( *pArrow );

                // GetInfo().Y() must be current baseline.
                SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTxtFrm()->Frm().Bottom();
                if( ( nDiff > 0 &&
                      ( GetEnd() < GetInfo().GetTxt().Len() ||
                        ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) ||
                    (nDiff >= 0 && bNextUndersized) )

                {
                    SwArrowPortion aArrow( GetInfo() );
                    GetInfo().DrawRedArrow( aArrow );
                }

                GetInfo().Y( nOldY );
			}
		}
	}

    if( pCurr->IsClipping() )
        rClip.ChgClip( rPaint, pFrm );
}

void SwTxtPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
                                          long nAdjustBaseLine )
{
    // Check if common underline should not be continued.
    if ( IsUnderlineBreak( *pPor, *pFnt ) )
    {
        // delete underline font
        delete GetInfo().GetUnderFnt();
        GetInfo().SetUnderFnt( 0 );
        return;
    }

    // If current underline matches the common underline font, we continue
    // to use the common underline font.
    if ( GetInfo().GetUnderFnt() &&
         GetInfo().GetUnderFnt()->GetFont().GetUnderline() ==
         GetFnt()->GetUnderline() )
         return;

    // calculate the new common underline font
    SwFont* pUnderlineFnt = 0;
    Point aCommonBaseLine;

    Range aRange( 0, GetInfo().GetTxt().Len() );
    MultiSelection aUnderMulti( aRange );

    ASSERT( GetFnt() && UNDERLINE_NONE != GetFnt()->GetUnderline(),
            "CheckSpecialUnderline without underlined font" )
    const SwFont* pParaFnt = GetAttrHandler().GetFont();
    if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() )
        aUnderMulti.SelectAll();

    SwTxtAttr* pTxtAttr;
    if( HasHints() )
	{
        sal_Bool bUnder = sal_False;
		MSHORT nTmp = 0;

        while( nTmp < pHints->GetStartCount() )
		{
			pTxtAttr = pHints->GetStart( nTmp++ );
            sal_Bool bUnderSelect = sal_False;

            const SvxUnderlineItem* pItem =
                    static_cast<const SvxUnderlineItem*>(CharFmt::GetItem( *pTxtAttr, RES_CHRATR_UNDERLINE ));

            if ( pItem )
            {
                bUnder = sal_True;
                bUnderSelect = pFnt->GetUnderline() == pItem->GetLineStyle();
            }

            if( bUnder )
			{
				xub_StrLen nSt = *pTxtAttr->GetStart();
				xub_StrLen nEnd = *pTxtAttr->GetEnd();
				if( nEnd > nSt )
				{
					Range aTmp( nSt, nEnd - 1 );
					if( bUnder )
						aUnderMulti.Select( aTmp, bUnderSelect );
				}
				bUnder = sal_False;
			}
		}
    }

    MSHORT i;
    xub_StrLen nIndx = GetInfo().GetIdx();
    long nUnderStart = 0;
    long nUnderEnd = 0;
    MSHORT nCnt = (MSHORT)aUnderMulti.GetRangeCount();

    // find the underline range the current portion is contained in
    for( i = 0; i < nCnt; ++i )
    {
        const Range& rRange = aUnderMulti.GetRange( i );
        if( nUnderEnd == rRange.Min() )
            nUnderEnd = rRange.Max();
        else if( nIndx >= rRange.Min() )
        {
            nUnderStart = rRange.Min();
            nUnderEnd = rRange.Max();
        }
        else
            break;
    }

    // restrict start and end to current line
    if ( GetStart() > nUnderStart )
        nUnderStart = GetStart();

    if ( GetEnd() && GetEnd() <= nUnderEnd )
        nUnderEnd = GetEnd() - 1;


    // check, if underlining is not isolated
    if ( nIndx + GetInfo().GetLen() < nUnderEnd + 1 )
    {
        //
        // here starts the algorithm for calculating the underline font
        //
        SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
        SwAttrIter aIter( *(SwTxtNode*)GetInfo().GetTxtFrm()->GetTxtNode(),
                          rScriptInfo );

        xub_StrLen nTmpIdx = nIndx;
        sal_uLong nSumWidth = 0;
        sal_uLong nSumHeight = 0;
        sal_uLong nBold = 0;
        sal_uInt16 nMaxBaseLineOfst = 0;
        sal_uInt16 nNumberOfPortions = 0;

        while( nTmpIdx <= nUnderEnd && pPor )
        {
            if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() ||
                pPor->IsBreakPortion() || pPor->IsMarginPortion() ||
                pPor->IsHolePortion() ||
                ( pPor->IsMultiPortion() && ! ((SwMultiPortion*)pPor)->IsBidi() ) )
                break;

            aIter.Seek( nTmpIdx );

            if ( aIter.GetFnt()->GetEscapement() < 0 || pFnt->IsWordLineMode() ||
                 SVX_CASEMAP_KAPITAELCHEN == pFnt->GetCaseMap() )
                break;

            if ( !aIter.GetFnt()->GetEscapement() )
            {
                nSumWidth += pPor->Width();
                const sal_uLong nFontHeight = aIter.GetFnt()->GetHeight();

                // If we do not have a common baseline we take the baseline
                // and the font of the lowest portion.
                if ( nAdjustBaseLine )
                {
                    sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *pCurr, pPor );
                    if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
                    {
                        nMaxBaseLineOfst = nTmpBaseLineOfst;
                        nSumHeight = nFontHeight;
                    }
                }
                // in horizontal layout we build a weighted sum of the heights
                else
                    nSumHeight += pPor->Width() * nFontHeight;

                if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
                    nBold += pPor->Width();
            }

            ++nNumberOfPortions;

            nTmpIdx = nTmpIdx + pPor->GetLen();
            pPor = pPor->GetPortion();
        }

        // resulting height
        if ( nNumberOfPortions > 1 && nSumWidth )
        {
            const sal_uLong nNewFontHeight = nAdjustBaseLine ?
                                         nSumHeight :
                                         nSumHeight / nSumWidth;

            pUnderlineFnt = new SwFont( *GetInfo().GetFont() );

            // font height
            const sal_uInt8 nActual = pUnderlineFnt->GetActual();
            pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(),
                                          nNewFontHeight ), nActual );

            // font weight
            if ( 2 * nBold > nSumWidth )
                pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual );
            else
                pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );

            // common base line
            aCommonBaseLine.Y() = nAdjustBaseLine + nMaxBaseLineOfst;
        }
    }

    // an escaped redlined portion should also have a special underlining
    if( ! pUnderlineFnt && pFnt->GetEscapement() > 0 && GetRedln() &&
        GetRedln()->ChkSpecialUnderline() )
        pUnderlineFnt = new SwFont( *pFnt );

    delete GetInfo().GetUnderFnt();

    if ( pUnderlineFnt )
    {
        pUnderlineFnt->SetProportion( 100 );
        pUnderlineFnt->SetEscapement( 0 );
        pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE );
        pUnderlineFnt->SetOverline( UNDERLINE_NONE );
        const Color aFillColor( COL_TRANSPARENT );
        pUnderlineFnt->SetFillColor( aFillColor );

        GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt,
                                                     aCommonBaseLine ) );
    }
    else
        // I'm sorry, we do not have a special underlining font for you.
        GetInfo().SetUnderFnt( 0 );
}
