/**************************************************************
 * 
 * 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"


#ifndef _OUTDEV_HXX //autogen
#include <vcl/outdev.hxx>
#endif
#ifndef _PRINT_HXX //autogen
#include <vcl/print.hxx>
#endif
#include <vcl/lineinfo.hxx>
#ifndef _METRIC_HXX //autogen
#include <vcl/metric.hxx>
#endif
#include <vcl/window.hxx>
#include <vcl/svapp.hxx>
#ifndef _COM_SUN_STAR_I18N_CHARACTERITERATORMODE_HDL_
#include <com/sun/star/i18n/CharacterIteratorMode.hdl>
#endif
#ifndef _COM_SUN_STAR_I18N_WORDTYPE_HDL
#include <com/sun/star/i18n/WordType.hdl>
#endif
#include <breakit.hxx>
#include <viewsh.hxx>		// Bildschirmabgleich
#include <viewopt.hxx>		// Bildschirmabgleich abschalten, ViewOption
#include <fntcache.hxx>
#include <IDocumentSettingAccess.hxx>
#include <swfont.hxx>       // CH_BLANK + CH_BULLET
#include <wrong.hxx>
#include "dbg_lay.hxx"
#include <txtfrm.hxx>       // SwTxtFrm
#include <pagefrm.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <tgrditem.hxx>
#include <scriptinfo.hxx>
#include <editeng/brshitem.hxx>
#include <tools/shl.hxx>
#include <swmodule.hxx>
#include <accessibilityoptions.hxx>
#include <svtools/accessibilityoptions.hxx>
#include <doc.hxx>
#include <editeng/fhgtitem.hxx>
#include <docsh.hxx>
#ifndef _POOLFMT_HRC
#include <poolfmt.hrc>
#endif

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

// globale Variablen, werden in FntCache.Hxx bekanntgegeben
// Der FontCache wird in TxtInit.Cxx _TXTINIT erzeugt und in _TXTEXIT geloescht
SwFntCache *pFntCache = NULL;
// Letzter Font, der durch ChgFntCache eingestellt wurde.
SwFntObj *pLastFont = NULL;
// Die "MagicNumber", die den Fonts zur Identifizierung verpasst wird
sal_uInt8* pMagicNo = NULL;

Color *pWaveCol = 0;

long SwFntObj::nPixWidth;
MapMode* SwFntObj::pPixMap = NULL;
OutputDevice* SwFntObj::pPixOut = NULL;

extern sal_uInt16 UnMapDirection( sal_uInt16 nDir, const sal_Bool bVertFormat );
sal_uInt16 GetDefaultFontHeight( SwDrawTextInfo &rInf )
{
    SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell();
    SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool();

    String aString(SW_RES(STR_POOLCOLL_STANDARD));

    SfxStyleSheetBase* pStyle = pBasePool->Find( aString, (SfxStyleFamily)SFX_STYLE_FAMILY_PARA );
    SfxItemSet& aTmpSet = pStyle->GetItemSet();
    SvxFontHeightItem &aDefaultFontItem = (SvxFontHeightItem&)aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE);
    return (sal_uInt16)aDefaultFontItem.GetHeight();
}



/*************************************************************************
|*
|*	SwFntCache::Flush()
|*
|*	Ersterstellung		AMA 16. Dez. 94
|*	Letzte Aenderung	AMA 16. Dez. 94
|*
|*************************************************************************/

void SwFntCache::Flush( )
{
	if ( pLastFont )
	{
		pLastFont->Unlock();
		pLastFont = NULL;
	}
	SwCache::Flush( );
}

/*************************************************************************
|*
|*	SwFntObj::SwFntObj(), ~SwFntObj()
|*
|*	Ersterstellung		AMA 7. Nov. 94
|*	Letzte Aenderung	AMA 7. Nov. 94
|*
|*************************************************************************/

SwFntObj::SwFntObj( const SwSubFont &rFont, const void *pOwn, ViewShell *pSh ) :
    SwCacheObj( (void*)pOwn ),
	aFont( rFont ),
	pScrFont( NULL ),
	pPrtFont( &aFont ),
	pPrinter( NULL ),
	nPropWidth( rFont.GetPropWidth() )
{
	nZoom = pSh ? pSh->GetViewOptions()->GetZoom() : USHRT_MAX;
    nGuessedLeading = USHRT_MAX;
    nExtLeading = USHRT_MAX;
	nPrtAscent = USHRT_MAX;
	nPrtHeight = USHRT_MAX;
	bPaintBlank = ( UNDERLINE_NONE != aFont.GetUnderline()
				 || UNDERLINE_NONE != aFont.GetOverline()
				 || STRIKEOUT_NONE != aFont.GetStrikeout() )
				 && !aFont.IsWordLineMode();
	aFont.SetLanguage(rFont.GetLanguage());
}

SwFntObj::~SwFntObj()
{
	if ( pScrFont != pPrtFont )
		delete pScrFont;
	if ( pPrtFont != &aFont )
		delete pPrtFont;
}

void SwFntObj::CreatePrtFont( const OutputDevice& rPrt )
{
    if ( nPropWidth != 100 && pPrinter != &rPrt )
    {
        if( pScrFont != pPrtFont )
            delete pScrFont;
        if( pPrtFont != &aFont )
            delete pPrtFont;

        const Font aOldFnt( rPrt.GetFont() );
        ((OutputDevice&)rPrt).SetFont( aFont );
        const FontMetric aWinMet( rPrt.GetFontMetric() );
        ((OutputDevice&)rPrt).SetFont( aOldFnt );
        long nWidth = ( aWinMet.GetSize().Width() * nPropWidth ) / 100;

        if( !nWidth )
            ++nWidth;
        pPrtFont = new Font( aFont );
        pPrtFont->SetSize( Size( nWidth, aFont.GetSize().Height() ) );
        pScrFont = NULL;
    }
}

/*************************************************************************
 *
 *  bool lcl_IsFontAdjustNecessary( rOutDev, rRefDev )
 *
 *  returns whether we have to adjust the output font to resemble
 *  the formatting font
 *
 *  _Not_ necessary if
 *
 *  1. RefDef == OutDev (text formatting, online layout...)
 *  2. PDF export from online layout
 *  3. Prospect/PagePreview pringing
 *
 *************************************************************************/

bool lcl_IsFontAdjustNecessary( const OutputDevice& rOutDev,
                                const OutputDevice& rRefDev )
{
    return &rRefDev != &rOutDev &&
           OUTDEV_WINDOW != rRefDev.GetOutDevType() &&
           ( OUTDEV_PRINTER != rRefDev.GetOutDevType() ||
             OUTDEV_PRINTER != rOutDev.GetOutDevType() );
}

struct CalcLinePosData
{
    SwDrawTextInfo& rInf;
    Font& rFont;
    xub_StrLen nCnt;
    const sal_Bool bSwitchH2V;
    const sal_Bool bSwitchL2R;
    long nHalfSpace;
    sal_Int32* pKernArray;
    const sal_Bool bBidiPor;

    CalcLinePosData( SwDrawTextInfo& _rInf, Font& _rFont,
                      xub_StrLen _nCnt, const sal_Bool _bSwitchH2V, const sal_Bool _bSwitchL2R,
                      long _nHalfSpace, sal_Int32* _pKernArray, const sal_Bool _bBidiPor) :
        rInf( _rInf ),
        rFont( _rFont ),
        nCnt( _nCnt ),
        bSwitchH2V( _bSwitchH2V ),
        bSwitchL2R( _bSwitchL2R ),
        nHalfSpace( _nHalfSpace ),
        pKernArray( _pKernArray ),
        bBidiPor( _bBidiPor )
    {
    }
};

/** Function: lcl_calcLinePos

   Computes the start and end position of an underline. This function is called
   from the DrawText-method (for underlining misspelled words or smarttag terms).
*/

void lcl_calcLinePos( const CalcLinePosData &rData,
    Point &rStart, Point &rEnd, xub_StrLen nStart, xub_StrLen nWrLen )
{
   long nBlank = 0;
   const xub_StrLen nEnd = nStart + nWrLen;
   const long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR;

   if ( nEnd < rData.nCnt
       && CH_BLANK == rData.rInf.GetText().GetChar( rData.rInf.GetIdx() + nEnd ) )
   {
       if( nEnd + 1 == rData.nCnt )
           nBlank -= nTmpSpaceAdd;
       else
           nBlank -= rData.nHalfSpace;
   }

   // determine start, end and length of wave line
   sal_Int32 nKernStart = nStart ? rData.pKernArray[ sal_uInt16( nStart - 1 ) ] : 0;
   sal_Int32 nKernEnd = rData.pKernArray[ sal_uInt16( nEnd - 1 ) ];

   sal_uInt16 nDir = rData.bBidiPor ? 1800 :
       UnMapDirection( rData.rFont.GetOrientation(), rData.bSwitchH2V );

   switch ( nDir )
   {
   case 0 :
       rStart.X() += nKernStart;
       rEnd.X() = nBlank + rData.rInf.GetPos().X() + nKernEnd;
       rEnd.Y() = rData.rInf.GetPos().Y();
       break;
   case 900 :
       rStart.Y() -= nKernStart;
       rEnd.X() = rData.rInf.GetPos().X();
       rEnd.Y() = nBlank + rData.rInf.GetPos().Y() - nKernEnd;
       break;
   case 1800 :
       rStart.X() -= nKernStart;
       rEnd.X() = rData.rInf.GetPos().X() - nKernEnd - nBlank;
       rEnd.Y() = rData.rInf.GetPos().Y();
       break;
   case 2700 :
       rStart.Y() += nKernStart;
       rEnd.X() = rData.rInf.GetPos().X();
       rEnd.Y() = nBlank + rData.rInf.GetPos().Y() + nKernEnd;
       break;
   }

   if ( rData.bSwitchL2R )
   {
       rData.rInf.GetFrm()->SwitchLTRtoRTL( rStart );
       rData.rInf.GetFrm()->SwitchLTRtoRTL( rEnd );
   }

   if ( rData.bSwitchH2V )
   {
       rData.rInf.GetFrm()->SwitchHorizontalToVertical( rStart );
       rData.rInf.GetFrm()->SwitchHorizontalToVertical( rEnd );
   }
}

/*************************************************************************
 *
 *  sal_uInt16 SwFntObj::GetFontAscent( const OutputDevice& rOut )
 *
 *	Ersterstellung		AMA 7. Nov. 94
 *	Letzte Aenderung	AMA 7. Nov. 94
 *
 *  Beschreibung: liefern den Ascent des Fonts auf dem
 * 	gewuenschten Outputdevice zurueck, ggf. muss der Bildschirmfont erst
 *  erzeugt werden.
 *************************************************************************/

sal_uInt16 SwFntObj::GetFontAscent( const ViewShell *pSh, const OutputDevice& rOut )
{
    sal_uInt16 nRet = 0;
    const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;

    if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
    {
        CreateScrFont( *pSh, rOut );
        ASSERT( USHRT_MAX != nScrAscent, "nScrAscent is going berzerk" )
        nRet = nScrAscent;
	}
    else
	{
		if ( nPrtAscent == USHRT_MAX ) // DruckerAscent noch nicht bekannt?
		{
            CreatePrtFont( rOut );
            const Font aOldFnt( rRefDev.GetFont() );
            ((OutputDevice&)rRefDev).SetFont( *pPrtFont );
            const FontMetric aOutMet( rRefDev.GetFontMetric() );
			nPrtAscent = (sal_uInt16) aOutMet.GetAscent();
            ( (OutputDevice&)rRefDev).SetFont( aOldFnt );
		}

        nRet = nPrtAscent;
	}

#if !defined(MACOSX) // #i89844# extleading is below the line for Mac
    // TODO: move extleading below the line for all platforms too
    nRet += GetFontLeading( pSh, rRefDev );
#endif

    ASSERT( USHRT_MAX != nRet, "GetFontAscent returned USHRT_MAX" )
    return nRet;
}

/*************************************************************************
 *
 *  sal_uInt16 SwFntObj::GetFontHeight( const OutputDevice* pOut )
 *
 *  Ersterstellung      AMA 7. Nov. 94
 *  Letzte Aenderung    AMA 7. Nov. 94
 *
 *  Beschreibung: liefern die H?he des Fonts auf dem
 *  gewuenschten Outputdevice zurueck, ggf. muss der Bildschirmfont erst
 *  erzeugt werden.
 *************************************************************************/

sal_uInt16 SwFntObj::GetFontHeight( const ViewShell* pSh, const OutputDevice& rOut )
{
    sal_uInt16 nRet = 0;
    const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;

    if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
    {
        CreateScrFont( *pSh, rOut );
        ASSERT( USHRT_MAX != nScrHeight, "nScrHeight is going berzerk" )
        nRet = nScrHeight + GetFontLeading( pSh, rRefDev );
    }
    else
	{
		if ( nPrtHeight == USHRT_MAX ) // PrinterHeight noch nicht bekannt?
		{
            CreatePrtFont( rOut );
            const Font aOldFnt( rRefDev.GetFont() );
            ((OutputDevice&)rRefDev).SetFont( *pPrtFont );
            nPrtHeight = static_cast<sal_uInt16>(rRefDev.GetTextHeight());

#if OSL_DEBUG_LEVEL > 1
            // Check if vcl did not change the meading of GetTextHeight
            const FontMetric aOutMet( rRefDev.GetFontMetric() );
            long nTmpPrtHeight = (sal_uInt16)aOutMet.GetAscent() + aOutMet.GetDescent();
            (void) nTmpPrtHeight;
            // #i106098#: do not compare with == here due to rounding error
            ASSERT( abs(nTmpPrtHeight - nPrtHeight) < 3,
                    "GetTextHeight != Ascent + Descent" );
#endif

            ((OutputDevice&)rRefDev).SetFont( aOldFnt );
		}

        nRet = nPrtHeight + GetFontLeading( pSh, rRefDev );
	}

    ASSERT( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" )
    return nRet;
}

sal_uInt16 SwFntObj::GetFontLeading( const ViewShell *pSh, const OutputDevice& rOut )
{
    sal_uInt16 nRet = 0;

    if ( pSh )
    {
        if ( USHRT_MAX == nGuessedLeading || USHRT_MAX == nExtLeading )
        {
            const Font aOldFnt( rOut.GetFont() );
            ((OutputDevice&)rOut).SetFont( *pPrtFont );
            const FontMetric aMet( rOut.GetFontMetric() );
            ((OutputDevice&)rOut).SetFont( aOldFnt );
            bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
            GuessLeading( *pSh, aMet );
            nExtLeading = static_cast<sal_uInt16>(aMet.GetExtLeading());
        }

        const IDocumentSettingAccess& rIDSA = *pSh->getIDocumentSettingAccess();
        const bool bBrowse = ( pSh->GetWin() &&
                               pSh->GetViewOptions()->getBrowseMode() &&
                              !pSh->GetViewOptions()->IsPrtFormat() );

        if ( !bBrowse && rIDSA.get(IDocumentSettingAccess::ADD_EXT_LEADING) )
            nRet = nExtLeading;
        else
            nRet = nGuessedLeading;
    }

    ASSERT( USHRT_MAX != nRet, "GetFontLeading returned USHRT_MAX" )
    return nRet;
}


/*************************************************************************
 *
 *  SwFntObj::CreateScrFont( const ViewShell& rSh, const OutputDevice& rOut )
 *
 *	Ersterstellung		AMA 7. Nov. 94
 *	Letzte Aenderung	AMA 7. Nov. 94
 *
 *  pOut is the output device, not the reference device
 *
 *************************************************************************/

void SwFntObj::CreateScrFont( const ViewShell& rSh, const OutputDevice& rOut )
{
    if ( pScrFont )
        return;

    // any changes to the output device are reset at the end of the function
    OutputDevice* pOut = (OutputDevice*)&rOut;

    // Save old font
    Font aOldOutFont( pOut->GetFont() );

    nScrHeight = USHRT_MAX;

    // Condition for output font / refdev font adjustment
    OutputDevice* pPrt = &rSh.GetRefDev();

    if( !rSh.GetWin() ||
        !rSh.GetViewOptions()->getBrowseMode() ||
         rSh.GetViewOptions()->IsPrtFormat() )
    {
        // After CreatePrtFont pPrtFont is the font which is actually used
        // by the reference device
        CreatePrtFont( *pPrt );
        pPrinter = pPrt;

        // save old reference device font
        Font aOldPrtFnt( pPrt->GetFont() );

        // set the font used at the reference device at the reference device
        // and the output device
		pPrt->SetFont( *pPrtFont );
        pOut->SetFont( *pPrtFont );

        // This should be the default for pScrFont.
        pScrFont = pPrtFont;

        FontMetric aMet = pPrt->GetFontMetric( );
        //Don't loose "faked" properties of the logical font that don't truly
        //exist in the physical font metrics which vcl which fake up for us
        aMet.SetWeight(pScrFont->GetWeight());
        aMet.SetItalic(pScrFont->GetItalic());

        bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();

        if ( USHRT_MAX == nGuessedLeading )
            GuessLeading( rSh, aMet );

        if ( USHRT_MAX == nExtLeading )
            nExtLeading = static_cast<sal_uInt16>(aMet.GetExtLeading());

        // reset the original reference device font
        pPrt->SetFont( aOldPrtFnt );
	}
	else
	{
		bSymbol = RTL_TEXTENCODING_SYMBOL == aFont.GetCharSet();
        if ( nGuessedLeading == USHRT_MAX )
            nGuessedLeading = 0;

        // no external leading in browse mode
        if ( nExtLeading == USHRT_MAX )
            nExtLeading = 0;

        pScrFont = pPrtFont;
    }

    // Zoomfaktor ueberpruefen, z.B. wg. PrtOle2 beim Speichern
	{
		// Sollte der Zoomfaktor des OutputDevices nicht mit dem der View-
		// Options uebereinstimmen, so darf dieser Font nicht gecacht
		// werden, deshalb wird der Zoomfaktor auf einen "ungueltigen" Wert
		// gesetzt.
		long nTmp;
		if( pOut->GetMapMode().GetScaleX().IsValid() &&
			pOut->GetMapMode().GetScaleY().IsValid() &&
			pOut->GetMapMode().GetScaleX() == pOut->GetMapMode().GetScaleY() )
		{
			nTmp = ( 100 * pOut->GetMapMode().GetScaleX().GetNumerator() ) /
					 pOut->GetMapMode().GetScaleX().GetDenominator();
		}
		else
			nTmp = 0;
		if( nTmp != nZoom )
			nZoom = USHRT_MAX - 1;
	}

    nScrAscent = (sal_uInt16)pOut->GetFontMetric().GetAscent();
    if ( USHRT_MAX == nScrHeight )
        nScrHeight = (sal_uInt16)pOut->GetTextHeight();

    // reset original output device font
    pOut->SetFont( aOldOutFont );
}


void SwFntObj::GuessLeading( const ViewShell&
#if defined(WNT) || defined(PM2)
                             rSh
#endif
                             , const FontMetric& rMet )
{
    // If leading >= 5, this seems to be enough leading.
    // Nothing has to be done.
    if ( rMet.GetIntLeading() >= 5 )
    {
        nGuessedLeading = 0;
        return;
    }

#if defined(WNT) || defined(PM2)
    OutputDevice *pWin = rSh.GetWin() ?
                         rSh.GetWin() :
                         GetpApp()->GetDefaultDevice();
	if ( pWin )
	{
		MapMode aTmpMap( MAP_TWIP );
		MapMode aOldMap = pWin->GetMapMode( );
		pWin->SetMapMode( aTmpMap );
		const Font aOldFnt( pWin->GetFont() );
		pWin->SetFont( *pPrtFont );
		const FontMetric aWinMet( pWin->GetFontMetric() );
		const sal_uInt16 nWinHeight = sal_uInt16( aWinMet.GetSize().Height() );
		if( pPrtFont->GetName().Search( aWinMet.GetName() ) < USHRT_MAX )
		{
			// Wenn das Leading auf dem Window auch 0 ist, dann
			// muss es auch so bleiben (vgl. StarMath!).
            long nTmpLeading = (long)aWinMet.GetIntLeading();
			 // einen Versuch haben wir noch wg. 31003:
			if( nTmpLeading <= 0 )
			{
				pWin->SetFont( rMet );
                nTmpLeading = (long)pWin->GetFontMetric().GetIntLeading();
				if( nTmpLeading < 0 )
                    nGuessedLeading = 0;
				else
                    nGuessedLeading = sal_uInt16(nTmpLeading);
			}
			else
			{
                nGuessedLeading = sal_uInt16(nTmpLeading);
				// Manta-Hack #50153#:
				// Wer beim Leading luegt, luegt moeglicherweise auch beim
				// Ascent/Descent, deshalb wird hier ggf. der Font ein wenig
				// tiefergelegt, ohne dabei seine Hoehe zu aendern.
				long nDiff = Min( rMet.GetDescent() - aWinMet.GetDescent(),
					aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading );
				if( nDiff > 0 )
				{
					ASSERT( nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" );
                    if ( nPrtAscent < USHRT_MAX )
                        nPrtAscent = nPrtAscent + (sal_uInt16)(( 2 * nDiff ) / 5);
				}
			}
		}
		else
		{
			// Wenn alle Stricke reissen, nehmen wir 15% der
			// Hoehe, ein von CL empirisch ermittelter Wert.
            nGuessedLeading = (nWinHeight * 15) / 100;
		}
		pWin->SetFont( aOldFnt );
		pWin->SetMapMode( aOldMap );
	}
	else
#endif
        nGuessedLeading = 0;
}

/*************************************************************************
 *
 *	void SwFntObj::SetDeviceFont( const OutputDevice *pOut ),
 *
 *	Ersterstellung		AMA 7. Nov. 94
 *	Letzte Aenderung	AMA 7. Nov. 94
 *
 *  Beschreibung: stellt den Font am gewuenschten OutputDevice ein,
 *  am Bildschirm muss eventuell erst den Abgleich durchgefuehrt werden.
 *
 *************************************************************************/

void SwFntObj::SetDevFont( const ViewShell *pSh, OutputDevice& rOut )
{
    const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;

    if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
    {
        CreateScrFont( *pSh, rOut );
        if( !GetScrFont()->IsSameInstance( rOut.GetFont() ) )
            rOut.SetFont( *pScrFont );
        if( pPrinter && ( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) )
            pPrinter->SetFont( *pPrtFont );
    }
    else
	{
        CreatePrtFont( rOut );
        if( !pPrtFont->IsSameInstance( rOut.GetFont() ) )
            rOut.SetFont( *pPrtFont );
	}

    // Here, we actually do not need the leading values, but by calling
    // GetFontLeading() we assure that the values are calculated for later use.
    GetFontLeading( pSh, rRefDev );
}

#define WRONG_SHOW_MIN 5
#define WRONG_SHOW_SMALL 11
#define WRONG_SHOW_MEDIUM 15

/*************************************************************************
 *
 * void SwFntObj::DrawText( ... )
 *
 *	Ersterstellung		AMA 16. Dez. 94
 *	Letzte Aenderung	AMA 16. Dez. 94
 *
 *  Beschreibung: Textausgabe
 * 					auf dem Bildschirm 			=> DrawTextArray
 * 					auf dem Drucker, !Kerning 	=> DrawText
 * 					auf dem Drucker + Kerning	=> DrawStretchText
 *
 *************************************************************************/

sal_uInt8 lcl_WhichPunctuation( xub_Unicode cChar )
{
    if ( ( cChar < 0x3001 || cChar > 0x3002 ) &&
            ( cChar < 0x3008 || cChar > 0x3011 ) &&
            ( cChar < 0x3014 || cChar > 0x301F ) &&
              0xFF62 != cChar && 0xFF63 != cChar )
        // no punctuation
        return SwScriptInfo::NONE;
    else if ( 0x3001 == cChar || 0x3002 == cChar ||
              0x3009 == cChar || 0x300B == cChar ||
              0x300D == cChar || 0x300F == cChar ||
              0x3011 == cChar || 0x3015 == cChar ||
              0x3017 == cChar || 0x3019 == cChar ||
              0x301B == cChar || 0x301E == cChar ||
              0x301F == cChar || 0xFF63 == cChar )
        // right punctuation
        return SwScriptInfo::SPECIAL_RIGHT;

    return SwScriptInfo::SPECIAL_LEFT;
}

static sal_Bool lcl_IsMonoSpaceFont( const OutputDevice& rOut )
{
    const String aStr1( xub_Unicode( 0x3008 ) );
    const String aStr2( xub_Unicode( 0x307C ) );
    const long nWidth1 = rOut.GetTextWidth( aStr1 );
    const long nWidth2 = rOut.GetTextWidth( aStr2 );
    return nWidth1 == nWidth2;
}

// ER 09.07.95 20:34
// mit -Ox Optimierung stuerzt's unter win95 ab
// JP 12.07.95: unter WNT auch (i386);       Alpha ??
// global optimization off
#ifdef _MSC_VER
#pragma optimize("g",off)
#endif

/* This helper structure (SwForbidden) contains the already marked parts of the string
    to avoid double lines (e.g grammar + spell check error) */

typedef std::vector< std::pair< xub_StrLen, xub_StrLen > > SwForbidden;

static void lcl_DrawLineForWrongListData(
    SwForbidden &rForbidden,
    const SwDrawTextInfo    &rInf,
    const SwWrongList       *pWList,
    const CalcLinePosData   &rCalcLinePosData,
    const Size              &rPrtFontSize )
{
    if (!pWList) return;

    xub_StrLen nStart = rInf.GetIdx();
    xub_StrLen nWrLen = rInf.GetLen();

    // check if respective data is available in the current text range
    if (!pWList->Check( nStart, nWrLen ))
    {
        return;
    }

    long nHght = rInf.GetOut().LogicToPixel( rPrtFontSize ).Height();

    // Draw wavy lines for spell and grammar errors only if font is large enough.
    // Lines for smart tags will always be drawn.
    if (pWList != rInf.GetSmartTags() && WRONG_SHOW_MIN >= nHght)
    {
        return;
    }

    SwForbidden::iterator pIter = rForbidden.begin();
    if (rInf.GetOut().GetConnectMetaFile())
        rInf.GetOut().Push();

    const Color aCol( rInf.GetOut().GetLineColor() );

    // iterate over all ranges stored in the respective SwWrongList
    do
    {
        nStart = nStart - rInf.GetIdx();

        const xub_StrLen nEnd = nStart + nWrLen;
        xub_StrLen nNext = nStart;
        while( nNext < nEnd )
        {
            while( pIter != rForbidden.end() && pIter->second <= nNext )
                ++pIter;

            xub_StrLen nNextStart = nNext;
            xub_StrLen nNextEnd = nEnd;

            if( pIter == rForbidden.end() || nNextEnd <= pIter->first )
            {
                // No overlapping mark up found
                std::pair< xub_StrLen, xub_StrLen > aNew;
                aNew.first = nNextStart;
                aNew.second = nNextEnd;
                rForbidden.insert( pIter, aNew );
                pIter = rForbidden.begin();
                nNext = nEnd;
            }
            else
            {
                nNext = pIter->second;
                if( nNextStart < pIter->first )
                {
                    nNextEnd = pIter->first;
                    pIter->first = nNextStart;
                }
                else
                    continue;
            }
            // determine line pos
            Point aStart( rInf.GetPos() );
            Point aEnd;
            lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart );


            sal_uInt16 wrongPos = pWList->GetWrongPos(nNextStart + rInf.GetIdx());

            const SwWrongArea* wrongArea = pWList->GetElement(wrongPos);

            if (wrongArea != 0)
            {
                if (WRONGAREA_DASHED == wrongArea->mLineType)
                {
                    rInf.GetOut().SetLineColor( wrongArea->mColor );

                    aStart.Y() +=30;
                    aEnd.Y() +=30;

                    LineInfo aLineInfo( LINE_DASH );
                    aLineInfo.SetDistance( 40 );
                    aLineInfo.SetDashLen( 1 );
                    aLineInfo.SetDashCount(1);

                    rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
                }
                else if (WRONGAREA_WAVE == wrongArea->mLineType)
                {
                    rInf.GetOut().SetLineColor( wrongArea->mColor );

                    // get wavy line type to use
                    sal_uInt16 nWave =
                        WRONG_SHOW_MEDIUM < nHght ? WAVE_NORMAL :
                        ( WRONG_SHOW_SMALL < nHght ? WAVE_SMALL : WAVE_FLAT );

                    rInf.GetOut().DrawWaveLine( aStart, aEnd, nWave );
                }
                else if (WRONGAREA_WAVE_NORMAL == wrongArea->mLineType)
                {
                    rInf.GetOut().SetLineColor( wrongArea->mColor );

                    rInf.GetOut().DrawWaveLine( aStart, aEnd, WAVE_NORMAL);
                }

                else if (WRONGAREA_WAVE_SMALL == wrongArea->mLineType)
                {
                    rInf.GetOut().SetLineColor( wrongArea->mColor );

                    rInf.GetOut().DrawWaveLine( aStart, aEnd, WAVE_SMALL);
                }
                else if (WRONGAREA_WAVE_FLAT == wrongArea->mLineType)
                {
                    rInf.GetOut().SetLineColor( wrongArea->mColor );

                    rInf.GetOut().DrawWaveLine( aStart, aEnd, WAVE_FLAT);
                }
            }
        }

        nStart = nEnd + rInf.GetIdx();
        nWrLen = rInf.GetIdx() + rInf.GetLen() - nStart;
    }
    while (nWrLen && pWList->Check( nStart, nWrLen ));

    rInf.GetOut().SetLineColor( aCol );

    if (rInf.GetOut().GetConnectMetaFile())
        rInf.GetOut().Pop();
}


void SwFntObj::DrawText( SwDrawTextInfo &rInf )
{
    ASSERT( rInf.GetShell(), "SwFntObj::DrawText without shell" )

    OutputDevice& rRefDev = rInf.GetShell()->GetRefDev();
    OutputDevice* pWin = rInf.GetShell()->GetWin();

    // true if pOut is the printer and the printer has been used for formatting
    const sal_Bool bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() &&
                      OUTDEV_PRINTER == rRefDev.GetOutDevType();
    const sal_Bool bBrowse = ( pWin &&
                           rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
                          !rInf.GetShell()->GetViewOptions()->IsPrtFormat() &&
                          !rInf.GetBullet() &&
                           ( rInf.GetSpace() || !rInf.GetKern() ) &&
                          !rInf.GetWrong() &&
                          !rInf.GetGrammarCheck() &&
                          !rInf.GetSmartTags() &&
                          !rInf.GetGreyWave() );

    // bDirectPrint indicates that we can enter the branch which calls
    // the DrawText functions instead of calling the DrawTextArray functions
    const sal_Bool bDirectPrint = bPrt || bBrowse;

    // Condition for output font / refdev font adjustment
    const sal_Bool bUseScrFont =
        lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev );

    Font* pTmpFont = bUseScrFont ? pScrFont : pPrtFont;

    //
    //  bDirectPrint and bUseScrFont should have these values:
    //
    //  Outdev / RefDef  | Printer | VirtPrinter | Window
    // ----------------------------------------------------
    //  Printer          | 1 - 0   | 0 - 1       | -
    // ----------------------------------------------------
    //  VirtPrinter/PDF  | 0 - 1   | 0 - 1       | -
    // ----------------------------------------------------
    //  Window/VirtWindow| 0 - 1   | 0 - 1       | 1 - 0
    //
    // Exception: During painting of a Writer OLE object, we do not have
    // a window. Therefore bUseSrcFont is always 0 in this case.
    //

#ifdef DBG_UTIL

    const sal_Bool bNoAdjust = bPrt ||
            (  pWin &&
               rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
              !rInf.GetShell()->GetViewOptions()->IsPrtFormat() );

    if ( OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() )
    {
        // Printer output
        if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 1 && bUseScrFont == 0, "Outdev Check failed" )
        }
        else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" )
        }
        else
        {
            ASSERT( sal_False, "Outdev Check failed" )
        }
    }
    else if ( OUTDEV_VIRDEV == rInf.GetOut().GetOutDevType() && ! pWin )
    {
        // PDF export
        if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" )
        }
        else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" )
        }
        else
        {
            ASSERT( sal_False, "Outdev Check failed" )
        }
    }
    else if ( OUTDEV_WINDOW == rInf.GetOut().GetOutDevType() ||
               ( OUTDEV_VIRDEV == rInf.GetOut().GetOutDevType() && pWin ) )
    {
        // Window or virtual window
        if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" )
        }
        else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" )
        }
        else if ( OUTDEV_WINDOW == rRefDev.GetOutDevType() )
        {
            ASSERT( bNoAdjust == 1 && bUseScrFont == 0, "Outdev Check failed" )
        }
        else
        {
            ASSERT( sal_False, "Outdev Check failed" )
        }
    }
    else
    {
            ASSERT( sal_False, "Outdev Check failed" )
    }

#endif

    // robust: better use the printer font instead of using no font at all
    ASSERT( pTmpFont, "No screen or printer font?" );
    if ( ! pTmpFont )
        pTmpFont = pPrtFont;

    // HACK: UNDERLINE_WAVE darf nicht mehr missbraucht werden, daher
    // wird die graue Wellenlinie des ExtendedAttributSets zunaechst
    // in der Fontfarbe erscheinen.

    const sal_Bool bSwitchH2V = rInf.GetFrm() && rInf.GetFrm()->IsVertical();
    const sal_Bool bSwitchL2R = rInf.GetFrm() && rInf.GetFrm()->IsRightToLeft() &&
                            ! rInf.IsIgnoreFrmRTL();
    const sal_uLong nMode = rInf.GetOut().GetLayoutMode();
    const sal_Bool bBidiPor = ( bSwitchL2R !=
                            ( 0 != ( TEXT_LAYOUT_BIDI_RTL & nMode ) ) );

    // be sure to have the correct layout mode at the printer
    if ( pPrinter )
    {
        pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
        pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
    }

    Point aPos( rInf.GetPos() );
    if( !bPrt )
    {
        if( rInf.GetpOut() != pPixOut || rInf.GetOut().GetMapMode() != *pPixMap )
        {
            *pPixMap = rInf.GetOut().GetMapMode();
            pPixOut = rInf.GetpOut();
            Size aTmp( 1, 1 );
            nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width();
        }

        aPos.X() += rInf.GetFrm()->IsRightToLeft() ? 0 : nPixWidth;
    }

    Color aOldColor( pTmpFont->GetColor() );
    sal_Bool bChgColor = rInf.ApplyAutoColor( pTmpFont );
    if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
        rInf.GetOut().SetFont( *pTmpFont );
    if ( bChgColor )
        pTmpFont->SetColor( aOldColor );

    if ( STRING_LEN == rInf.GetLen() )
        rInf.SetLen( rInf.GetText().Len() );


    //
    // ASIAN LINE AND CHARACTER GRID MODE START: snap to characters
    //

    if ( rInf.GetFrm() && rInf.SnapToGrid() && rInf.GetFont() &&
         SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
        {
            //for textgrid refactor
            //const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            const sal_uInt16 nGridWidth = GETGRIDWIDTH(pGrid, pDoc);
            sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()];

            if ( pPrinter )
                pPrinter->GetTextArray( rInf.GetText(), pKernArray,
                                        rInf.GetIdx(), rInf.GetLen() );
            else
                rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                            rInf.GetIdx(), rInf.GetLen() );

            long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen();

            const sal_uLong i = nWidthPerChar ?
                                ( nWidthPerChar - 1 ) / nGridWidth + 1:
                                1;

            nWidthPerChar = i * nGridWidth;

            // position of first character, we take the printer position
            long nCharWidth = pKernArray[ 0 ];
            sal_uLong nHalfWidth = nWidthPerChar / 2;

            long nNextFix;

            // punctuation characters are not centered
            xub_Unicode cChar = rInf.GetText().GetChar( rInf.GetIdx() );
            sal_uInt8 nType = lcl_WhichPunctuation( cChar );
            switch ( nType )
            {
            case SwScriptInfo::NONE :
                aPos.X() += ( nWidthPerChar - nCharWidth ) / 2;
                nNextFix = nCharWidth / 2;
                break;
            case SwScriptInfo::SPECIAL_RIGHT :
                nNextFix = nHalfWidth;
                break;
            default:
                aPos.X() += nWidthPerChar - nCharWidth;
                nNextFix = nCharWidth - nHalfWidth;
            }

            // calculate offsets
            for ( xub_StrLen j = 1; j < rInf.GetLen(); ++j )
            {
                long nScr = pKernArray[ j ] - pKernArray[ j - 1 ];
                nNextFix += nWidthPerChar;

                // punctuation characters are not centered
                cChar = rInf.GetText().GetChar( rInf.GetIdx() + j );
                nType = lcl_WhichPunctuation( cChar );
                switch ( nType )
                {
                case SwScriptInfo::NONE :
                    pKernArray[ j - 1 ] = nNextFix - ( nScr / 2 );
                    break;
                case SwScriptInfo::SPECIAL_RIGHT :
                    pKernArray[ j - 1 ] = nNextFix - nHalfWidth;
                    break;
                default:
                    pKernArray[ j - 1 ] = nNextFix + nHalfWidth - nScr;
                }
            }

            // the layout engine requires the total width of the output
            pKernArray[ rInf.GetLen() - 1 ] = rInf.GetWidth() -
                                              aPos.X() + rInf.GetPos().X() ;

            if ( bSwitchH2V )
                rInf.GetFrm()->SwitchHorizontalToVertical( aPos );

            rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                pKernArray, rInf.GetIdx(), rInf.GetLen() );

            delete[] pKernArray;
            return;
        }
    }

    // For text grid refactor
    // ASIAN LINE AND CHARACTER GRID MODE START: not snap to characters
    //
    if ( rInf.GetFrm() && rInf.SnapToGrid() && rInf.GetFont() &&
         SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )

        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
        {
            const sal_uInt16  nDefaultFontHeight = GetDefaultFontHeight( rInf );

			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc);
            if( SW_LATIN == rInf.GetFont()->GetActual() )
                nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2;
            else
                nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight;

            sal_Int32*  pKernArray = new sal_Int32[rInf.GetLen()];

            if ( pPrinter )
                pPrinter->GetTextArray( rInf.GetText(), pKernArray,
                rInf.GetIdx(), rInf.GetLen() );
            else
                rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                rInf.GetIdx(), rInf.GetLen() );
            if ( bSwitchH2V )
                rInf.GetFrm()->SwitchHorizontalToVertical( aPos );
            if ( rInf.GetSpace() || rInf.GetKanaComp())
            {
                long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
                sal_Bool bSpecialJust = sal_False;
                if ( rInf.GetFont() && rInf.GetLen() )
                {
                    const SwScriptInfo* pSI = rInf.GetScriptInfo();
                    const sal_uInt8 nActual = rInf.GetFont()->GetActual();
                    ///Kana Compression
                    if( SW_CJK == nActual && rInf.GetKanaComp() &&
                        pSI && pSI->CountCompChg() &&
                        lcl_IsMonoSpaceFont( *(rInf.GetpOut()) ) )
                    {
                        pSI->Compress( pKernArray,rInf.GetIdx(), rInf.GetLen(),
                            rInf.GetKanaComp(), (sal_uInt16)aFont.GetSize().Height(),&aPos );
                        bSpecialJust = sal_True;
                    }
                    ///Asian Justification
                    if ( ( SW_CJK == nActual || SW_LATIN == nActual ) && nSpaceAdd )
                    {
                        LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK );
                        if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang)
                        {
                            long nSpaceSum = nSpaceAdd;
                            for ( sal_uInt16 nI = 0; nI < rInf.GetLen(); ++nI )
                            {
                                pKernArray[ nI ] += nSpaceSum;
                                nSpaceSum += nSpaceAdd;
                            }
                            bSpecialJust = sal_True;
                            nSpaceAdd = 0;
                        }
                    }
                    long nGridAddSum = nGridWidthAdd;
                    for(xub_StrLen i = 0; i < rInf.GetLen(); i++,nGridAddSum += nGridWidthAdd )
                    {
                        pKernArray[i] += nGridAddSum;
                    }
                    long nKernSum = rInf.GetKern();
                    if ( bSpecialJust || rInf.GetKern() )
                    {
                        for( xub_StrLen i = 0; i < rInf.GetLen(); i++, nKernSum += rInf.GetKern() )
                        {
                            if ( CH_BLANK == rInf.GetText().GetChar(rInf.GetIdx()+i) )
                                nKernSum += nSpaceAdd;
                            pKernArray[i] += nKernSum;
                        }
                        ///With through/uderstr. Grouped style requires a blank at the end
                        ///of a text edition special measures:
                        if( bPaintBlank && rInf.GetLen() && (CH_BLANK ==
                            rInf.GetText().GetChar( rInf.GetIdx() + rInf.GetLen() - 1) ) )
                        {
                            ///If it concerns a singular, underlined space acts,
                            ///we must spend two:
                            if( 1 == rInf.GetLen() )
                            {
                                pKernArray[0] = rInf.GetWidth() + nSpaceAdd;
                                rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                                    pKernArray, rInf.GetIdx(), 1 );
                            }
                            else
                            {
                                pKernArray[ rInf.GetLen() - 2] += nSpaceAdd;
                                rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                                    pKernArray, rInf.GetIdx(), rInf.GetLen() );
                            }
                        }
                        else
                        {
                            rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                                pKernArray, rInf.GetIdx(), rInf.GetLen() );
                        }
                    }
                    else
                    {
                        Point aTmpPos( aPos );
                        xub_StrLen i;
                        xub_StrLen j = 0;
                        long nSpaceSum = 0;
                        for( i = 0; i < rInf.GetLen(); i++ )
                        {
                            if( CH_BLANK == rInf.GetText().GetChar( rInf.GetIdx() + i) )
                            {
                                nSpaceSum += nSpaceAdd;
                                if( j < i)
                                    rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
                                    rInf.GetIdx() + j, i - j );
                                j = i + 1;
                                pKernArray[i] = pKernArray[i] + nSpaceSum;
                                aTmpPos.X() = aPos.X() + pKernArray[ i ] + nKernSum;
                            }
                        }
                        if( j < i )
                            rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
                            rInf.GetIdx() +j , i - j );
                    }
                }
            }
            else
            {
                //long nKernAdd = rInf.GetKern();
		long nKernAdd = 0;
                long nGridAddSum = nGridWidthAdd + nKernAdd;
                for(xub_StrLen i = 0; i < rInf.GetLen(); i++,nGridAddSum += nGridWidthAdd + nKernAdd )
                {
                    pKernArray[i] += nGridAddSum;
                }
                rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                    pKernArray, rInf.GetIdx(), rInf.GetLen() );
            }
            delete[] pKernArray;
            return;
        }
    }

    //
    // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT
    //

    if ( bDirectPrint )
    {
        const Fraction aTmp( 1, 1 );
        sal_Bool bStretch = rInf.GetWidth() && ( rInf.GetLen() > 1 ) && bPrt
                        && ( aTmp != rInf.GetOut().GetMapMode().GetScaleX() );

        if ( bSwitchL2R )
            rInf.GetFrm()->SwitchLTRtoRTL( aPos );

        if ( bSwitchH2V )
            rInf.GetFrm()->SwitchHorizontalToVertical( aPos );

        // In the good old days we used to have a simple DrawText if the
        // output device is the printer. Now we need a DrawTextArray if
        // 1. KanaCompression is enabled
        // 2. Justified alignment
        // Simple kerning is handled by DrawStretchText
        if( rInf.GetSpace() || rInf.GetKanaComp() )
        {
            sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ];
            rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                       rInf.GetIdx(), rInf.GetLen() );

            if( bStretch )
            {
                xub_StrLen nZwi = rInf.GetLen() - 1;
                long nDiff = rInf.GetWidth() - pKernArray[ nZwi ]
                             - rInf.GetLen() * rInf.GetKern();
                long nRest = nDiff % nZwi;
                long nAdd;
                if( nRest < 0 )
                {
                    nAdd = -1;
                    nRest += nZwi;
                }
                else
                {
                    nAdd = +1;
                    nRest = nZwi - nRest;
                }
                nDiff /= nZwi;
                long nSum = nDiff;
                for( xub_StrLen i = 0; i < nZwi; )
                {
                    pKernArray[ i ] += nSum;
                    if( ++i == nRest )
                        nDiff += nAdd;
                    nSum += nDiff;
                }
            }

            //
            // Modify Array for special justifications
            //
            long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
            sal_Bool bSpecialJust = sal_False;

            if ( rInf.GetFont() && rInf.GetLen() )
            {
                const SwScriptInfo* pSI = rInf.GetScriptInfo();
                const sal_uInt8 nActual = rInf.GetFont()->GetActual();

                // Kana Compression
                if ( SW_CJK == nActual && rInf.GetKanaComp() &&
                     pSI && pSI->CountCompChg() &&
                     lcl_IsMonoSpaceFont( rInf.GetOut() ) )
                {
                    pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(),
                                   rInf.GetKanaComp(),
                                   (sal_uInt16)aFont.GetSize().Height(), &aPos );
                    bSpecialJust = sal_True;
                }

                // Asian Justification
                if ( SW_CJK == nActual && nSpaceAdd )
                {
                    LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK );

                    if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
                    {
                        long nSpaceSum = nSpaceAdd;
                        for ( sal_uInt16 nI = 0; nI < rInf.GetLen(); ++nI )
                        {
                            pKernArray[ nI ] += nSpaceSum;
                            nSpaceSum += nSpaceAdd;
                        }

                        bSpecialJust = sal_True;
                        nSpaceAdd = 0;
                    }
                }

                // Kashida Justification
                if ( SW_CTL == nActual && nSpaceAdd )
                {
                    if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
                    {
                        if ( pSI && pSI->CountKashida() &&
                            pSI->KashidaJustify( pKernArray, 0, rInf.GetIdx(),
                                                 rInf.GetLen(), nSpaceAdd ) != STRING_LEN )
                        {
                            bSpecialJust = sal_True;
                            nSpaceAdd = 0;
                        }
                    }
                }

                // Thai Justification
                if ( SW_CTL == nActual && nSpaceAdd )
                {
                    LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL );

                    if ( LANGUAGE_THAI == aLang )
                    {
                        // Use rInf.GetSpace() because it has more precision than
                        // nSpaceAdd:
                        SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray, 0,
                                                   rInf.GetIdx(), rInf.GetLen(),
                                                   rInf.GetNumberOfBlanks(),
                                                   rInf.GetSpace() );

                        // adding space to blanks is already done
                        bSpecialJust = sal_True;
                        nSpaceAdd = 0;
                    }
                }
            }

            long nKernSum = rInf.GetKern();

            if ( bStretch || bPaintBlank || rInf.GetKern() || bSpecialJust )
            {
                for( xub_StrLen i = 0; i < rInf.GetLen(); i++,
                     nKernSum += rInf.GetKern() )
                {
                    if ( CH_BLANK == rInf.GetText().GetChar(rInf.GetIdx()+i) )
                        nKernSum += nSpaceAdd;
                    pKernArray[i] += nKernSum;
                }

				// Bei durch/unterstr. Blocksatz erfordert ein Blank am Ende
				// einer Textausgabe besondere Massnahmen:
				if( bPaintBlank && rInf.GetLen() && ( CH_BLANK ==
					rInf.GetText().GetChar( rInf.GetIdx()+rInf.GetLen()-1 ) ) )
				{
					// Wenn es sich um ein singulaeres, unterstrichenes Space
					// handelt, muessen wir zwei ausgeben:
					if( 1 == rInf.GetLen() )
					{
               			pKernArray[0] = rInf.GetWidth() + nSpaceAdd;

						rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
					                                 pKernArray, rInf.GetIdx(), 1 );
					}
					else
					{
                        pKernArray[ rInf.GetLen() - 2 ] += nSpaceAdd;
                        rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                            pKernArray, rInf.GetIdx(), rInf.GetLen() );
                    }
                }
                else
                    rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                                                 pKernArray, rInf.GetIdx(), rInf.GetLen() );
            }
            else
            {
                Point aTmpPos( aPos );
                xub_StrLen j = 0;
                xub_StrLen i;
                for( i = 0; i < rInf.GetLen(); i++ )
                {
                    if( CH_BLANK == rInf.GetText().GetChar( rInf.GetIdx()+i ) )
                    {
                        nKernSum += nSpaceAdd;
                        if( j < i )
                        rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
                                                rInf.GetIdx() + j, i - j );
                        j = i + 1;
                        SwTwips nAdd = pKernArray[ i ] + nKernSum;
                        if ( ( TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL ) == nMode )
                            nAdd *= -1;
                        aTmpPos.X() = aPos.X() + nAdd;
                    }
                }
                if( j < i )
                    rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
                                            rInf.GetIdx() + j, i - j );
            }
            delete[] pKernArray;
        }
        else if( bStretch )
        {
            long nTmpWidth = rInf.GetWidth();
            if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() )
                nTmpWidth -= rInf.GetKern();
            rInf.GetOut().DrawStretchText( aPos, nTmpWidth,
                                           rInf.GetText(), rInf.GetIdx(), rInf.GetLen() );
        }
        else if( rInf.GetKern() )
        {
            const long nTmpWidth = GetTextSize( rInf ).Width();

            const Color aSaveColor( pTmpFont->GetColor() );
            const sal_Bool bColorChanged = rInf.ApplyAutoColor( pTmpFont );

            if( bColorChanged )
            {
                if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
                    rInf.GetOut().SetFont( *pTmpFont );
                pTmpFont->SetColor( aSaveColor );
            }

            rInf.GetOut().DrawStretchText( aPos, (sal_uInt16)nTmpWidth,
                                           rInf.GetText(), rInf.GetIdx(), rInf.GetLen() );
        }
        else
            rInf.GetOut().DrawText( aPos, rInf.GetText(),
                                    rInf.GetIdx(), rInf.GetLen() );
    }

    //
    // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT
    //

    else
    {
        const String* pStr = &rInf.GetText();
        String aStr( aEmptyStr );
        sal_Bool bBullet = rInf.GetBullet();
        if( bSymbol )
            bBullet = sal_False;
        sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ];
        CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
        long nScrPos;

        // get screen array
        sal_Int32* pScrArray = new sal_Int32[ rInf.GetLen() ];
        rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray,
                                    rInf.GetIdx(), rInf.GetLen() );

        // OLE: no printer available
        // ASSERT( pPrinter, "DrawText needs pPrinter" )
        if ( pPrinter )
        {
            // pTmpFont has already been set as current font for rInf.GetOut()
            if ( pPrinter != rInf.GetpOut() || pTmpFont != pPrtFont )
            {
                if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) )
                    pPrinter->SetFont( *pPrtFont );
            }
            pPrinter->GetTextArray( rInf.GetText(), pKernArray, rInf.GetIdx(),
                                    rInf.GetLen() );
        }
        else
        {
//            sal_Bool bRestore = sal_False;
//            MapMode aOld( rInf.GetOut().GetMapMode() );
//                if( rInf.GetZoom().GetNumerator() &&
//                        rInf.GetZoom() != aOld.GetScaleX() )
//                {
//                        MapMode aNew( aOld );
//                        aNew.SetScaleX( rInf.GetZoom() );
//                        aNew.SetScaleY( rInf.GetZoom() );
//                        rInf.GetOut().SetMapMode( aNew );
//                        bRestore = sal_True;
//                }
            rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                        rInf.GetIdx(), rInf.GetLen() );
//            if( bRestore )
//                rInf.GetOut().SetMapMode( aOld );
        }

        //
        // Modify Printer and ScreenArrays for special justifications
        //
        long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
        bool bNoHalfSpace = false;

        if ( rInf.GetFont() && rInf.GetLen() )
        {
            const sal_uInt8 nActual = rInf.GetFont()->GetActual();
            const SwScriptInfo* pSI = rInf.GetScriptInfo();

            // Kana Compression
            if ( SW_CJK == nActual && rInf.GetKanaComp() &&
                 pSI && pSI->CountCompChg() &&
                 lcl_IsMonoSpaceFont( rInf.GetOut() ) )
            {
                Point aTmpPos( aPos );
                pSI->Compress( pScrArray, rInf.GetIdx(), rInf.GetLen(),
                               rInf.GetKanaComp(),
                               (sal_uInt16)aFont.GetSize().Height(), &aTmpPos );
                pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(),
                               rInf.GetKanaComp(),
                               (sal_uInt16)aFont.GetSize().Height(), &aPos );
            }

            // Asian Justification
            if ( SW_CJK == nActual && nSpaceAdd )
            {
                LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK );

                if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
                {
                    long nSpaceSum = nSpaceAdd;
                    for ( sal_uInt16 nI = 0; nI < rInf.GetLen(); ++nI )
                    {
                        pKernArray[ nI ] += nSpaceSum;
                        pScrArray[ nI ] += nSpaceSum;
                        nSpaceSum += nSpaceAdd;
                    }

                    nSpaceAdd = 0;
                }
            }

            // Kashida Justification
            if ( SW_CTL == nActual && nSpaceAdd )
            {
                if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
                {
                    if ( pSI && pSI->CountKashida() &&
                         pSI->KashidaJustify( pKernArray, pScrArray, rInf.GetIdx(),
                                              rInf.GetLen(), nSpaceAdd ) != STRING_LEN )
                        nSpaceAdd = 0;
                    else
                        bNoHalfSpace = true;
                }
            }

            // Thai Justification
            if ( SW_CTL == nActual && nSpaceAdd )
            {
                LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL );

                if ( LANGUAGE_THAI == aLang )
                {
                    SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray,
                                               pScrArray, rInf.GetIdx(),
                                               rInf.GetLen(),
                                               rInf.GetNumberOfBlanks(),
                                               rInf.GetSpace() );

                    // adding space to blanks is already done
                    nSpaceAdd = 0;
                }
            }
        }

        nScrPos = pScrArray[ 0 ];

        if( bBullet )
        {
            // !!! HACK !!!
            // The Arabic layout engine requires some context of the string
            // which should be painted.
            xub_StrLen nCopyStart = rInf.GetIdx();
            if ( nCopyStart )
                --nCopyStart;

            xub_StrLen nCopyLen = rInf.GetLen();
            if ( nCopyStart + nCopyLen < rInf.GetText().Len() )
                ++nCopyLen;

            aStr = rInf.GetText().Copy( nCopyStart, nCopyLen );
            pStr = &aStr;

            for( xub_StrLen i = 0; i < aStr.Len(); ++i )
                if( CH_BLANK == aStr.GetChar( i ) )
                    aStr.SetChar( i, CH_BULLET );
        }

		xub_StrLen nCnt = rInf.GetText().Len();
		if ( nCnt < rInf.GetIdx() )
			nCnt = 0;
		else
			nCnt = nCnt - rInf.GetIdx();
		nCnt = Min( nCnt, rInf.GetLen() );
		long nKernSum = rInf.GetKern();
		xub_Unicode cChPrev = rInf.GetText().GetChar( rInf.GetIdx() );

		// Wenn es sich um ein singulaeres, unterstrichenes Space
		// im Blocksatz handelt, muessen wir zwei ausgeben:
		if ( ( nCnt == 1 ) && rInf.GetSpace() && ( cChPrev == CH_BLANK ) )
		{
            pKernArray[0] = rInf.GetWidth() +
                            rInf.GetKern() +
                          ( rInf.GetSpace() / SPACING_PRECISION_FACTOR );

            if ( bSwitchL2R )
                rInf.GetFrm()->SwitchLTRtoRTL( aPos );

            if ( bSwitchH2V )
                rInf.GetFrm()->SwitchHorizontalToVertical( aPos );

            rInf.GetOut().DrawTextArray( aPos, rInf.GetText(),
                                         pKernArray, rInf.GetIdx(), 1 );
			if( bBullet )
				rInf.GetOut().DrawTextArray( aPos, *pStr, pKernArray,
                                             rInf.GetIdx() ? 1 : 0, 1 );
        }
        else
        {
            xub_Unicode nCh;

            // Bei Pairkerning waechst der Printereinfluss auf die Positionierung
            sal_uInt16 nMul = 3;

            if ( pPrtFont->GetKerning() )
                nMul = 1;

            const sal_uInt16 nDiv = nMul+1;

            // In nSpaceSum wird der durch Blocksatz auf die Spaces verteilte
            // Zwischenraum aufsummiert.
            // Die Spaces selbst werden im Normalfall in der Mitte des
            // Zwischenraums positioniert, deshalb die nSpace/2-Mimik.
            // Bei wortweiser Unterstreichung muessen sie am Anfang des
            // Zwischenraums stehen, damit dieser nicht unterstrichen wird.
            // Ein Space am Anfang oder am Ende des Textes muss allerdings
            // vor bzw. hinter den kompletten Zwischenraum gesetzt werden,
            // sonst wuerde das Durch-/Unterstreichen Luecken aufweisen.
            long nSpaceSum = 0;
            // in word line mode and for Arabic, we disable the half space trick:
            const long nHalfSpace = pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2;
            const long nOtherHalf = nSpaceAdd - nHalfSpace;
            if ( nSpaceAdd && ( cChPrev == CH_BLANK ) )
                nSpaceSum = nHalfSpace;
            for ( xub_StrLen i=1; i<nCnt; ++i,nKernSum += rInf.GetKern() )
            {
                nCh = rInf.GetText().GetChar( rInf.GetIdx() + i );

                ASSERT( pScrArray, "Where is the screen array?" )
                long nScr;
                nScr = pScrArray[ i ] - pScrArray[ i - 1 ];

                // Wenn vor uns ein (Ex-)SPACE ist, positionieren wir uns optimal,
                // d.h. unseren rechten Rand auf die 100% Druckerposition,
                // sind wir sogar selbst ein Ex-SPACE, so positionieren wir uns
                // linksbuendig zur Druckerposition.
                if ( nCh == CH_BLANK )
                {
#ifdef FONT_TEST_DEBUG
                    lcl_Pos( 3, nScrPos, nScr, pKernArray[i-1], pKernArray[i] );
#else
                    nScrPos = pKernArray[i-1] + nScr;
#endif
                    if ( cChPrev == CH_BLANK )
                        nSpaceSum += nOtherHalf;
                    if ( i + 1 == nCnt )
                        nSpaceSum += nSpaceAdd;
                    else
                        nSpaceSum += nHalfSpace;
                }
                else
                {
                    if ( cChPrev == CH_BLANK )
                    {
#ifdef FONT_TEST_DEBUG
                        lcl_Pos( 6, nScrPos, nScr, pKernArray[i-1], pKernArray[i] );
#else
                        nScrPos = pKernArray[i-1] + nScr;
#endif
                        // kein Pixel geht verloren:
                        nSpaceSum += nOtherHalf;
                    }
                    else if ( cChPrev == '-' )
#ifdef FONT_TEST_DEBUG
                        lcl_Pos( 6, nScrPos, nScr, pKernArray[i-1], pKernArray[i] );
#else
                        nScrPos = pKernArray[i-1] + nScr;
#endif
                    else
                    {
#ifdef FONT_TEST_DEBUG
                        lcl_Pos( 0, nScrPos, nScr, pKernArray[i-1], pKernArray[i] );
#else
                        nScrPos += nScr;
                        nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
#endif
                    }
                }
                cChPrev = nCh;
                pKernArray[i-1] = nScrPos - nScr + nKernSum + nSpaceSum;
                // In word line mode and for Arabic, we disabled the half space trick. If a portion
                // ends with a blank, the full nSpaceAdd value has been added to the character in
                // front of the blank. This leads to painting artifacts, therefore we remove the
                // nSpaceAdd value again:
				if ( (bNoHalfSpace || pPrtFont->IsWordLineMode()) && i+1 == nCnt && nCh == CH_BLANK )
					pKernArray[i-1] = pKernArray[i-1] - nSpaceAdd;
            }

            // the layout engine requires the total width of the output
            pKernArray[ rInf.GetLen() - 1 ] += nKernSum + nSpaceSum;

            if( rInf.GetGreyWave() )
            {
                if( rInf.GetLen() )
                {
                    long nHght = rInf.GetOut().LogicToPixel(
                                    pPrtFont->GetSize() ).Height();
                    if( WRONG_SHOW_MIN < nHght )
                    {
                        if ( rInf.GetOut().GetConnectMetaFile() )
                            rInf.GetOut().Push();

                        sal_uInt16 nWave =
                            WRONG_SHOW_MEDIUM < nHght ? WAVE_NORMAL :
                            ( WRONG_SHOW_SMALL < nHght ? WAVE_SMALL :
                            WAVE_FLAT );
                        Color aCol( rInf.GetOut().GetLineColor() );
                        sal_Bool bColSave = aCol != *pWaveCol;
                        if ( bColSave )
                            rInf.GetOut().SetLineColor( *pWaveCol );

                        Point aEnd;
                        long nKernVal = pKernArray[ sal_uInt16( rInf.GetLen() - 1 ) ];

                        sal_uInt16 nDir = bBidiPor ?
                                        1800 :
                                        UnMapDirection(
                                            GetFont()->GetOrientation(),
                                            bSwitchH2V );

                        switch ( nDir )
                        {
                        case 0 :
                            aEnd.X() = rInf.GetPos().X() + nKernVal;
                            aEnd.Y() = rInf.GetPos().Y();
                            break;
                        case 900 :
                            aEnd.X() = rInf.GetPos().X();
                            aEnd.Y() = rInf.GetPos().Y() - nKernVal;
                            break;
                        case 1800 :
                            aEnd.X() = rInf.GetPos().X() - nKernVal;
                            aEnd.Y() = rInf.GetPos().Y();
                            break;
                        case 2700 :
                            aEnd.X() = rInf.GetPos().X();
                            aEnd.Y() = rInf.GetPos().Y() + nKernVal;
                            break;
                        }

                        Point aCurrPos( rInf.GetPos() );

                        if ( bSwitchL2R )
                        {
                            rInf.GetFrm()->SwitchLTRtoRTL( aCurrPos );
                            rInf.GetFrm()->SwitchLTRtoRTL( aEnd );
                        }

                        if ( bSwitchH2V )
                        {
                            rInf.GetFrm()->SwitchHorizontalToVertical( aCurrPos );
                            rInf.GetFrm()->SwitchHorizontalToVertical( aEnd );
                        }
                        rInf.GetOut().DrawWaveLine( aCurrPos, aEnd, nWave );

                        if ( bColSave )
                            rInf.GetOut().SetLineColor( aCol );

                        if ( rInf.GetOut().GetConnectMetaFile() )
                            rInf.GetOut().Pop();
                    }
                }
            }
            else if( !bSymbol && rInf.GetLen() )
            {
				// anything to do?
				if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags())
				{
					CalcLinePosData aCalcLinePosData(rInf, *GetFont(),
							nCnt, bSwitchH2V, bSwitchL2R,
							nHalfSpace, pKernArray, bBidiPor);

                    SwForbidden aForbidden;
					// draw line for smart tag data
					lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() );
                    // draw wave line for spell check errors
                    // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict.
                    // reason: some grammar errors can only be found if spelling errors are fixed,
                    // therefore we don't want the user to miss a spelling error.
                    lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, pPrtFont->GetSize() );
					// draw wave line for grammar check errors
					lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, pPrtFont->GetSize() );
				}
            }

            xub_StrLen nOffs = 0;
            xub_StrLen nLen = rInf.GetLen();
#ifdef COMING_SOON
            if( aPos.X() < rInf.GetLeft() )
            {
                while( nOffs < nLen &&
                    aPos.X() + pKernArray[ nOffs ] < rInf.GetLeft() )
                    ++nOffs;
                if( nOffs < nLen )
                {
                    --nLen;
                    while( nLen > nOffs &&
                        aPos.X() + pKernArray[ nLen ] > rInf.GetRight() )
                        --nLen;
                    ++nLen;
                    if( nOffs )
                        --nOffs;
                }
                if( nOffs )
                {
                    long nDiff = pKernArray[ nOffs - 1 ];
                    aPos.X() += nDiff;
                    for( xub_StrLen nX = nOffs; nX < nLen; ++nX )
                        pKernArray[ nX ] -= nDiff;
                }
            }
#endif
            if( nOffs < nLen )
            {
                // If we paint bullets instead of spaces, we use a copy of
                // the paragraph string. For the layout engine, the copy
                // of the string has to be an environment of the range which
                // is painted
                xub_StrLen nTmpIdx = bBullet ?
                                              ( rInf.GetIdx() ? 1 : 0 ) :
                                              rInf.GetIdx();

                if ( bSwitchL2R )
                    rInf.GetFrm()->SwitchLTRtoRTL( aPos );

                if ( bSwitchH2V )
                    rInf.GetFrm()->SwitchHorizontalToVertical( aPos );

                rInf.GetOut().DrawTextArray( aPos, *pStr, pKernArray + nOffs,
                                             nTmpIdx + nOffs , nLen - nOffs );
            }
        }
        delete[] pScrArray;
        delete[] pKernArray;
    }
}


// Optimierung war fuer DrawText() ausgeschaltet
#ifdef _MSC_VER
#pragma optimize("",on)
#endif


/*************************************************************************
 *
 *	Size SwFntObj::GetTextSize( const OutputDevice *pOut, const String &rTxt,
 *			 const sal_uInt16 nIdx, const sal_uInt16 nLen, const short nKern = 0 );
 *
 *	Ersterstellung		AMA 16. Dez. 94
 *	Letzte Aenderung	AMA 16. Dez. 94
 *
 *  Beschreibung: ermittelt die TextSize (des Druckers)
 *
 *************************************************************************/

Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf )
{
	Size aTxtSize;
	const xub_StrLen nLn = ( STRING_LEN != rInf.GetLen() ) ? rInf.GetLen() :
						   rInf.GetText().Len();

    // be sure to have the correct layout mode at the printer
    if ( pPrinter )
    {
        pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
        pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
    }

    if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
         SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
        {
			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            const sal_uInt16 nGridWidth = GETGRIDWIDTH(pGrid, pDoc);

            OutputDevice* pOutDev;

            if ( pPrinter )
            {
                if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) )
                    pPrinter->SetFont(*pPrtFont);
                pOutDev = pPrinter;
            }
            else
                pOutDev = rInf.GetpOut();

            aTxtSize.Width() =
                    pOutDev->GetTextWidth( rInf.GetText(), rInf.GetIdx(), nLn );

            ASSERT( !rInf.GetShell() ||
                    ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExtLeading() ),
                "Leading values should be already calculated" )
            aTxtSize.Height() = pOutDev->GetTextHeight() +
                                GetFontLeading( rInf.GetShell(), rInf.GetOut() );

            long nWidthPerChar = aTxtSize.Width() / nLn;

            const sal_uLong i = nWidthPerChar ?
                            ( nWidthPerChar - 1 ) / nGridWidth + 1:
                            1;

            aTxtSize.Width() = i * nGridWidth * nLn;
            rInf.SetKanaDiff( 0 );
            return aTxtSize;
        }
    }

    //for textgrid refactor
    if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
         SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
        {
            const sal_uInt16 nDefaultFontHeight = GetDefaultFontHeight( rInf );

			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc);
            if( SW_LATIN == rInf.GetFont()->GetActual() )
                nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2;
            else
                nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight;
            OutputDevice* pOutDev;
            if ( pPrinter )
            {
                if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) )
                    pPrinter->SetFont(*pPrtFont);
                pOutDev = pPrinter;
            }
            else
                pOutDev = rInf.GetpOut();
            aTxtSize.Width() = pOutDev->GetTextWidth( rInf.GetText(), rInf.GetIdx(), nLn );
            aTxtSize.Height() = pOutDev->GetTextHeight() +
                                GetFontLeading( rInf.GetShell(), rInf.GetOut() );
            aTxtSize.Width() += (nLn) * long( nGridWidthAdd );
            //if ( rInf.GetKern() && nLn )
            //    aTxtSize.Width() += ( nLn ) * long( rInf.GetKern() );

            rInf.SetKanaDiff( 0 );
            return aTxtSize;
        }
    }

    const sal_Bool bCompress = rInf.GetKanaComp() && nLn &&
                           rInf.GetFont() &&
                           SW_CJK == rInf.GetFont()->GetActual() &&
                           rInf.GetScriptInfo() &&
                           rInf.GetScriptInfo()->CountCompChg() &&
                           lcl_IsMonoSpaceFont( rInf.GetOut() );

	ASSERT(	!bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
			CountCompChg()), "Compression without info" );

    // This is the part used e.g., for cursor travelling
    // See condition for DrawText or DrawTextArray (bDirectPrint)
    if ( pPrinter && pPrinter != rInf.GetpOut() )
	{
		if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) )
			pPrinter->SetFont(*pPrtFont);
		aTxtSize.Width() = pPrinter->GetTextWidth( rInf.GetText(),
                                                   rInf.GetIdx(), nLn );
		aTxtSize.Height() = pPrinter->GetTextHeight();
		sal_Int32 *pKernArray = new sal_Int32[nLn];
        CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
        if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) )
            rInf.GetOut().SetFont( *pScrFont );
		long nScrPos;

        pPrinter->GetTextArray( rInf.GetText(), pKernArray, rInf.GetIdx(),nLn );
		if( bCompress )
            rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray,
            	rInf.GetIdx(), nLn, rInf.GetKanaComp(),
				(sal_uInt16)aFont.GetSize().Height() ) );
		else
			rInf.SetKanaDiff( 0 );

        if ( rInf.GetKanaDiff() )
            nScrPos = pKernArray[ nLn - 1 ];
        else
        {
            sal_Int32* pScrArray = new sal_Int32[ rInf.GetLen() ];
            rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray,
                                        rInf.GetIdx(), rInf.GetLen() );
            nScrPos = pScrArray[ 0 ];
            xub_StrLen nCnt = rInf.GetText().Len();
            if ( nCnt < rInf.GetIdx() )
                nCnt=0;
            else
                nCnt = nCnt - rInf.GetIdx();
            nCnt = Min (nCnt, nLn);
            xub_Unicode nChPrev = rInf.GetText().GetChar( rInf.GetIdx() );

            xub_Unicode nCh;

            // Bei Pairkerning waechst der Printereinfluss auf die Positionierung
            sal_uInt16 nMul = 3;
            if ( pPrtFont->GetKerning() )
                nMul = 1;
            const sal_uInt16 nDiv = nMul+1;
            for( xub_StrLen i=1; i<nCnt; i++ )
            {
                nCh = rInf.GetText().GetChar( rInf.GetIdx() + i );
                long nScr;
                nScr = pScrArray[ i ] - pScrArray[ i - 1 ];
                if ( nCh == CH_BLANK )
                    nScrPos = pKernArray[i-1]+nScr;
                else
                {
                    if ( nChPrev == CH_BLANK || nChPrev == '-' )
                        nScrPos = pKernArray[i-1]+nScr;
                    else
                    {
                        nScrPos += nScr;
                        nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
                    }
                }
                nChPrev = nCh;
                pKernArray[i-1] = nScrPos - nScr;
            }
            delete[] pScrArray;
        }

        delete[] pKernArray;
		aTxtSize.Width() = nScrPos;
	}
	else
	{
        if( !pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) )
            rInf.GetOut().SetFont( *pPrtFont );
		if( bCompress )
		{
			sal_Int32 *pKernArray = new sal_Int32[nLn];
            rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                        rInf.GetIdx(), nLn );
            rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray,
            	rInf.GetIdx(), nLn, rInf.GetKanaComp(),
				(sal_uInt16) aFont.GetSize().Height() ) );
			aTxtSize.Width() = pKernArray[ nLn - 1 ];
			delete[] pKernArray;
		}
		else
		{
            aTxtSize.Width() = rInf.GetOut().GetTextWidth( rInf.GetText(),
                                                           rInf.GetIdx(), nLn );
			rInf.SetKanaDiff( 0 );
		}

        aTxtSize.Height() = rInf.GetOut().GetTextHeight();
	}

	if ( rInf.GetKern() && nLn )
		aTxtSize.Width() += ( nLn - 1 ) * long( rInf.GetKern() );

    ASSERT( !rInf.GetShell() ||
            ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExtLeading() ),
              "Leading values should be already calculated" )
    aTxtSize.Height() += GetFontLeading( rInf.GetShell(), rInf.GetOut() );
	return aTxtSize;
}


xub_StrLen SwFntObj::GetCrsrOfst( SwDrawTextInfo &rInf )
{
    long nSpaceAdd =       rInf.GetSpace() / SPACING_PRECISION_FACTOR;
    const long nSperren = -rInf.GetSperren() / SPACING_PRECISION_FACTOR;
    long nKern = rInf.GetKern();

    if( 0 != nSperren )
        nKern -= nSperren;

    sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ];

    // be sure to have the correct layout mode at the printer
    if ( pPrinter )
    {
        pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
        pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
        pPrinter->GetTextArray( rInf.GetText(), pKernArray,
                                rInf.GetIdx(), rInf.GetLen() );
    }
	else
        rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                    rInf.GetIdx(), rInf.GetLen() );

    const SwScriptInfo* pSI = rInf.GetScriptInfo();
    if ( rInf.GetFont() && rInf.GetLen() )
    {
        const sal_uInt8 nActual = rInf.GetFont()->GetActual();

        // Kana Compression
        if ( SW_CJK == nActual && rInf.GetKanaComp() &&
             pSI && pSI->CountCompChg() &&
             lcl_IsMonoSpaceFont( rInf.GetOut() ) )
        {
            pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(),
                           rInf.GetKanaComp(),
                           (sal_uInt16) aFont.GetSize().Height() );
        }

        // Asian Justification
        if ( SW_CJK == rInf.GetFont()->GetActual() )
        {
            LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK );

            if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
            {
                long nSpaceSum = nSpaceAdd;
                for ( sal_uInt16 nI = 0; nI < rInf.GetLen(); ++nI )
                {
                    pKernArray[ nI ] += nSpaceSum;
                    nSpaceSum += nSpaceAdd;
                }

                nSpaceAdd = 0;
            }

        }

        // Kashida Justification
        if ( SW_CTL == nActual && rInf.GetSpace() )
        {
            if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
            {
                if ( pSI && pSI->CountKashida() &&
                    pSI->KashidaJustify( pKernArray, 0, rInf.GetIdx(), rInf.GetLen(),
                                         nSpaceAdd ) != STRING_LEN )
                    nSpaceAdd = 0;
            }
        }

        // Thai Justification
        if ( SW_CTL == nActual && nSpaceAdd )
        {
            LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL );

            if ( LANGUAGE_THAI == aLang )
            {
                SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray, 0,
                                           rInf.GetIdx(), rInf.GetLen(),
                                           rInf.GetNumberOfBlanks(),
                                           rInf.GetSpace() );

                // adding space to blanks is already done
                nSpaceAdd = 0;
            }
        }
    }

	long nLeft = 0;
	long nRight = 0;
	xub_StrLen nCnt = 0;
    long nSpaceSum = 0;
	long nKernSum = 0;

    if ( rInf.GetFrm() && rInf.GetLen() && rInf.SnapToGrid() &&
         rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
        {
			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            const sal_uInt16 nGridWidth = GETGRIDWIDTH(pGrid, pDoc);

            long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen();

            sal_uLong i = nWidthPerChar ?
                      ( nWidthPerChar - 1 ) / nGridWidth + 1:
                      1;

            nWidthPerChar = i * nGridWidth;

            nCnt = (sal_uInt16)(rInf.GetOfst() / nWidthPerChar);
            if ( 2 * ( rInf.GetOfst() - nCnt * nWidthPerChar ) > nWidthPerChar )
                ++nCnt;

            delete[] pKernArray;
            return nCnt;
        }
    }

    //for textgrid refactor
    if ( rInf.GetFrm() && rInf.GetLen() && rInf.SnapToGrid() &&
         rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
        {

            const sal_uInt16 nDefaultFontHeight = GetDefaultFontHeight( rInf );

			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc);
            if( SW_LATIN == rInf.GetFont()->GetActual() )
                nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2;
            else
                nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight;

            for(xub_StrLen j = 0; j < rInf.GetLen(); j++)
            {
                long nScr = pKernArray[ j ] + ( nSpaceAdd + nGridWidthAdd  ) * ( j + 1 );
                if( nScr >= rInf.GetOfst())
                {
                    nCnt = j;
                    break;
                }
            }
            delete[] pKernArray;
            return nCnt;
        }
    }

    sal_uInt16 nItrMode = i18n::CharacterIteratorMode::SKIPCELL;
    sal_Int32 nDone = 0;
    LanguageType aLang = LANGUAGE_NONE;
    bool bSkipCharacterCells = false;
    xub_StrLen nIdx = rInf.GetIdx();
    xub_StrLen nLastIdx = nIdx;
    const xub_StrLen nEnd = rInf.GetIdx() + rInf.GetLen();

    // --> OD 2009-12-29 #i105901#
    // skip character cells for all script types
    if ( pBreakIt->GetBreakIter().is() )
    // <--
    {
        aLang = rInf.GetFont()->GetLanguage();
        bSkipCharacterCells = true;
    }

    while ( ( nRight < long( rInf.GetOfst() ) ) && ( nIdx < nEnd ) )
	{
        if ( nSpaceAdd && CH_BLANK == rInf.GetText().GetChar( nIdx ) )
            nSpaceSum += nSpaceAdd;

        // go to next character (cell).
        nLastIdx = nIdx;

        if ( bSkipCharacterCells )
        {
            nIdx = (xub_StrLen)pBreakIt->GetBreakIter()->nextCharacters( rInf.GetText(),
                        nIdx, pBreakIt->GetLocale( aLang ), nItrMode, 1, nDone );
            if ( nIdx <= nLastIdx )
                break;
        }
        else
            ++nIdx;

		nLeft = nRight;
        nRight = pKernArray[ nIdx - rInf.GetIdx() - 1 ] + nKernSum + nSpaceSum;

        nKernSum += nKern;
	}

    // step back if position is before the middle of the character
    // or if we do not want to go to the next character
    if ( nIdx > rInf.GetIdx() &&
         ( rInf.IsPosMatchesBounds() ||
           ( ( nRight > long( rInf.GetOfst() ) ) &&
             ( nRight - rInf.GetOfst() > rInf.GetOfst() - nLeft ) ) ) )
        nCnt = nLastIdx - rInf.GetIdx(); // first half
    else
        nCnt = nIdx - rInf.GetIdx(); // second half

    if ( pSI )
        rInf.SetCursorBidiLevel( pSI->DirType( nLastIdx ) );

	delete[] pKernArray;
	return nCnt;
}


/*************************************************************************
|*
|*	SwFntAccess::SwFntAccess()
|*
|*	Ersterstellung		AMA 9. Nov. 94
|*	Letzte Aenderung	AMA 9. Nov. 94
|*
|*************************************************************************/

SwFntAccess::SwFntAccess( const void* &rMagic,
                sal_uInt16 &rIndex, const void *pOwn, ViewShell *pSh,
				sal_Bool bCheck ) :
  SwCacheAccess( *pFntCache, rMagic, rIndex ),
  pShell( pSh )
{
	// Der benutzte CTor von SwCacheAccess sucht anhand rMagic+rIndex im Cache
	if ( IsAvail() )
	{
		// Der schnellste Fall: ein bekannter Font ( rMagic ),
		// bei dem Drucker und Zoom nicht ueberprueft werden brauchen.
		if ( !bCheck )
			return;

		// Hier ist zwar der Font bekannt, muss aber noch ueberprueft werden.

	}
	else
		// Hier ist der Font nicht bekannt, muss also gesucht werden.
		bCheck = sal_False;


    {
        OutputDevice* pOut = 0;
		sal_uInt16 nZoom = USHRT_MAX;

        // Get the reference device
        if ( pSh )
        {
            pOut = &pSh->GetRefDev();
            nZoom = pSh->GetViewOptions()->GetZoom();
        }

		SwFntObj *pFntObj;
		if ( bCheck )
		{
            pFntObj = Get();
			if ( ( pFntObj->GetZoom( ) == nZoom ) &&
				 ( pFntObj->pPrinter == pOut ) &&
				   pFntObj->GetPropWidth() ==
                        ((SwSubFont*)pOwn)->GetPropWidth() )
				return; // Die Ueberpruefung ergab: Drucker+Zoom okay.
			pFntObj->Unlock( ); // Vergiss dies Objekt, es wurde leider
			pObj = NULL;	 	// eine Drucker/Zoomaenderung festgestellt.
		}

        // Search by font comparison, quite expensive!
        // Look for same font and same printer
        pFntObj = pFntCache->First();
        while ( pFntObj && !( pFntObj->aFont == *(Font *)pOwn &&
                              pFntObj->GetZoom() == nZoom &&
                              pFntObj->GetPropWidth() ==
                              ((SwSubFont*)pOwn)->GetPropWidth() &&
                              ( !pFntObj->pPrinter || pFntObj->pPrinter == pOut ) ) )
			pFntObj = pFntCache->Next( pFntObj );

		if( pFntObj && pFntObj->pPrinter != pOut )
		{
			// Wir haben zwar einen ohne Drucker gefunden, mal sehen, ob es
			// auch noch einen mit identischem Drucker gibt.
			SwFntObj *pTmpObj = pFntObj;
            while( pTmpObj && !( pTmpObj->aFont == *(Font *)pOwn &&
				   pTmpObj->GetZoom()==nZoom && pTmpObj->pPrinter==pOut &&
				   pTmpObj->GetPropWidth() ==
                        ((SwSubFont*)pOwn)->GetPropWidth() ) )
				pTmpObj = pFntCache->Next( pTmpObj );
			if( pTmpObj )
				pFntObj = pTmpObj;
		}

        if ( !pFntObj ) // Font has not been found, create one
		{
			// Das Objekt muss neu angelegt werden, deshalb muss der Owner ein
			// SwFont sein, spaeter wird als Owner die "MagicNumber" gehalten.
            SwCacheAccess::pOwner = pOwn;
			pFntObj = Get(); // hier wird via NewObj() angelegt und gelockt.
			ASSERT(pFntObj, "No Font, no Fun.");
		}
        else  // Font has been found, so we lock it.
		{
			pFntObj->Lock();
			if( pFntObj->pPrinter != pOut ) // Falls bis dato kein Drucker bekannt
			{
				ASSERT( !pFntObj->pPrinter, "SwFntAccess: Printer Changed" );
                pFntObj->CreatePrtFont( *pOut );
				pFntObj->pPrinter = pOut;
				pFntObj->pScrFont = NULL;
                pFntObj->nGuessedLeading = USHRT_MAX;
                pFntObj->nExtLeading = USHRT_MAX;
				pFntObj->nPrtAscent = USHRT_MAX;
				pFntObj->nPrtHeight = USHRT_MAX;
			}
			pObj = pFntObj;
		}

		// egal, ob neu oder gefunden, ab jetzt ist der Owner vom Objekt eine
		// MagicNumber und wird auch dem aufrufenden SwFont bekanntgegeben,
		// ebenso der Index fuer spaetere direkte Zugriffe
		rMagic = pFntObj->GetOwner();
		SwCacheAccess::pOwner = rMagic;
		rIndex = pFntObj->GetCachePos();
	}
}

SwCacheObj *SwFntAccess::NewObj( )
{
	// Ein neuer Font, eine neue "MagicNumber".
	return new SwFntObj( *(SwSubFont *)pOwner, ++pMagicNo, pShell );
}

extern xub_StrLen lcl_CalcCaseMap( const SwFont& rFnt,
                                   const XubString& rOrigString,
                                   xub_StrLen nOfst,
                                   xub_StrLen nLen,
                                   xub_StrLen nIdx );

xub_StrLen SwFont::GetTxtBreak( SwDrawTextInfo& rInf, long nTextWidth )
{
    ChgFnt( rInf.GetShell(), rInf.GetOut() );

    const sal_Bool bCompress = rInf.GetKanaComp() && rInf.GetLen() &&
                           SW_CJK == GetActual() &&
                           rInf.GetScriptInfo() &&
                           rInf.GetScriptInfo()->CountCompChg() &&
                           lcl_IsMonoSpaceFont( rInf.GetOut() );

    ASSERT( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
            CountCompChg()), "Compression without info" );

	sal_uInt16 nTxtBreak = 0;
	long nKern = 0;

	sal_uInt16 nLn = ( rInf.GetLen() == STRING_LEN ? rInf.GetText().Len()
											   : rInf.GetLen() );

    if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() &&
         rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
        {
			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            const sal_uInt16 nGridWidth = GETGRIDWIDTH(pGrid, pDoc);

            sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()];
            rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                        rInf.GetIdx(), rInf.GetLen() );

            long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen();

            const sal_uLong i = nWidthPerChar ?
                            ( nWidthPerChar - 1 ) / nGridWidth + 1:
                            1;

            nWidthPerChar = i * nGridWidth;
            long nCurrPos = nWidthPerChar;

            while( nTxtBreak < rInf.GetLen() && nTextWidth >= nCurrPos )
            {
                nCurrPos += nWidthPerChar;
                ++nTxtBreak;
            }

            delete[] pKernArray;
            return nTxtBreak + rInf.GetIdx();
        }
    }

    //for text grid enhancement
    if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
         SW_CJK == rInf.GetFont()->GetActual() )
    {
        GETGRID( rInf.GetFrm()->FindPageFrm() )
        if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
        {
            const sal_uInt16 nDefaultFontHeight = GetDefaultFontHeight( rInf );

			const SwDoc* pDoc = rInf.GetShell()->GetDoc();
            long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc);
            if( SW_LATIN == rInf.GetFont()->GetActual() )
                nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2 ;
            else
                nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight;

            sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()];
            rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
                                            rInf.GetIdx(), rInf.GetLen() );
            long nCurrPos = pKernArray[nTxtBreak] + nGridWidthAdd;
            while( nTxtBreak < rInf.GetLen() && nTextWidth >= nCurrPos)
            {
                nTxtBreak++;
                nCurrPos = pKernArray[nTxtBreak] + nGridWidthAdd * ( nTxtBreak + 1 );
            }
            delete[] pKernArray;
            return nTxtBreak + rInf.GetIdx();
        }
    }

    if( aSub[nActual].IsCapital() && nLn )
		nTxtBreak = GetCapitalBreak( rInf.GetShell(), rInf.GetpOut(),
		rInf.GetScriptInfo(), rInf.GetText(), nTextWidth,0, rInf.GetIdx(),nLn );
	else
	{
		nKern = CheckKerning();

        const XubString* pTmpText;
        XubString aTmpText;
        xub_StrLen nTmpIdx;
        xub_StrLen nTmpLen;
        bool bTextReplaced = false;

        if ( !aSub[nActual].IsCaseMap() )
        {
            pTmpText = &rInf.GetText();
            nTmpIdx = rInf.GetIdx();
            nTmpLen = nLn;
        }
        else
        {
			const XubString aSnippet( rInf.GetText(), rInf.GetIdx(), nLn );
            aTmpText = aSub[nActual].CalcCaseMap( aSnippet );
            const bool bTitle = SVX_CASEMAP_TITEL == aSub[nActual].GetCaseMap() &&
                                pBreakIt->GetBreakIter().is();

            // Uaaaaahhhh!!! In title case mode, we would get wrong results
            if ( bTitle && nLn )
            {
                // check if rInf.GetIdx() is begin of word
                if ( !pBreakIt->GetBreakIter()->isBeginWord(
                     rInf.GetText(), rInf.GetIdx(),
                     pBreakIt->GetLocale( aSub[nActual].GetLanguage() ),
                     i18n::WordType::ANYWORD_IGNOREWHITESPACES ) )
                {
                    // In this case, the beginning of aTmpText is wrong.
                    XubString aSnippetTmp( aSnippet, 0, 1 );
                    aSnippetTmp = aSub[nActual].CalcCaseMap( aSnippetTmp );
                    aTmpText.Erase( 0, aSnippetTmp.Len() );
                    aTmpText.Insert( aSnippet.GetChar( 0 ), 0 );
                }
            }

            pTmpText = &aTmpText;
            nTmpIdx = 0;
            nTmpLen = aTmpText.Len();
            bTextReplaced = true;
        }

       	if( rInf.GetHyphPos() )
            nTxtBreak = rInf.GetOut().GetTextBreak( *pTmpText, nTextWidth,
                                                    '-', *rInf.GetHyphPos(),
                                                     nTmpIdx, nTmpLen, nKern );
        else
            nTxtBreak = rInf.GetOut().GetTextBreak( *pTmpText, nTextWidth,
                                                    nTmpIdx, nTmpLen, nKern );

        if ( bTextReplaced && STRING_LEN != nTxtBreak )
        {
            if ( nTmpLen != nLn )
                nTxtBreak = lcl_CalcCaseMap( *this, rInf.GetText(),
                                             rInf.GetIdx(), nLn, nTxtBreak );
            else
                nTxtBreak = nTxtBreak + rInf.GetIdx();
        }
	}

    if ( ! bCompress )
        return nTxtBreak;

    nTxtBreak = nTxtBreak - rInf.GetIdx();

    if( nTxtBreak < nLn )
	{
        if( !nTxtBreak && nLn )
			nLn = 1;
		else if( nLn > 2 * nTxtBreak )
			nLn = 2 * nTxtBreak;
		sal_Int32 *pKernArray = new sal_Int32[ nLn ];
		rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray,
									rInf.GetIdx(), nLn );
        if( rInf.GetScriptInfo()->Compress( pKernArray, rInf.GetIdx(), nLn,
                            rInf.GetKanaComp(), (sal_uInt16)GetHeight( nActual ) ) )
		{
			long nKernAdd = nKern;
			xub_StrLen nTmpBreak = nTxtBreak;
			if( nKern && nTxtBreak )
				nKern *= nTxtBreak - 1;
			while( nTxtBreak<nLn && nTextWidth >= pKernArray[nTxtBreak] +nKern )
			{
				nKern += nKernAdd;
				++nTxtBreak;
			}
			if( rInf.GetHyphPos() )
				*rInf.GetHyphPos() += nTxtBreak - nTmpBreak; // It's not perfect
		}
		delete[] pKernArray;
    }
    nTxtBreak = nTxtBreak + rInf.GetIdx();

    return nTxtBreak;
}

extern Color aGlobalRetoucheColor;

sal_Bool SwDrawTextInfo::ApplyAutoColor( Font* pFont )
{
    const Font& rFnt = pFont ? *pFont : GetOut().GetFont();
    sal_Bool bPrt = GetShell() && ! GetShell()->GetWin();
    ColorData nNewColor = COL_BLACK;
    sal_Bool bChgFntColor = sal_False;
    sal_Bool bChgLineColor = sal_False;

    if( bPrt && GetShell() && GetShell()->GetViewOptions()->IsBlackFont() )
	{
        if ( COL_BLACK != rFnt.GetColor().GetColor() )
            bChgFntColor = sal_True;

        if ( (COL_BLACK != GetOut().GetLineColor().GetColor()) ||
             (COL_BLACK != GetOut().GetOverlineColor().GetColor()) )
            bChgLineColor = sal_True;
	}
    else
    {
        // FontColor has to be changed if:
        // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set
        // LineColor has to be changed if:
        // 1. IsAlwaysAutoColor is set

        bChgLineColor = ! bPrt && GetShell() &&
                GetShell()->GetAccessibilityOptions()->IsAlwaysAutoColor();

        bChgFntColor = COL_AUTO == rFnt.GetColor().GetColor() || bChgLineColor;

        if ( bChgFntColor )
        {
            // check if current background has a user defined setting
            const Color* pCol = GetFont() ? GetFont()->GetBackColor() : NULL;
            if( ! pCol || COL_TRANSPARENT == pCol->GetColor() )
            {
                const SvxBrushItem* pItem;
                SwRect aOrigBackRect;

                //UUUU
                drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

                /// OD 21.08.2002
                ///     consider, that [GetBackgroundBrush(...)] can set <pCol>
                ///     - see implementation in /core/layout/paintfrm.cxx
                /// OD 21.08.2002 #99657#
                ///     There is a user defined setting for the background, if there
                ///     is a background brush and its color is *not* "no fill"/"auto fill".
                if( GetFrm()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, sal_False ) )
                {
                    if ( !pCol )
                    {
                        pCol = &pItem->GetColor();
                    }

                    /// OD 30.08.2002 #99657#
                    /// determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
                    if ( pCol->GetColor() == COL_TRANSPARENT)
                        pCol = NULL;
                }
                else
                    pCol = NULL;
            }

            // no user defined color at paragraph or font background
            if ( ! pCol )
                pCol = &aGlobalRetoucheColor;

            if( GetShell() && GetShell()->GetWin() )
            {
                // here we determine the preferred window text color for painting
                const SwViewOption* pViewOption = GetShell()->GetViewOptions();
                if(pViewOption->IsPagePreview() &&
                        !SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews())
                    nNewColor = COL_BLACK;
                else
                    // we take the font color from the appearance page
                    nNewColor = SwViewOption::GetFontColor().GetColor();
            }

            // change painting color depending of dark/bright background
            Color aTmpColor( nNewColor );
            if ( pCol->IsDark() && aTmpColor.IsDark() )
                nNewColor = COL_WHITE;
            else if ( pCol->IsBright() && aTmpColor.IsBright() )
                nNewColor = COL_BLACK;
        }
    }

    if ( bChgFntColor || bChgLineColor )
    {
        Color aNewColor( nNewColor );

        if ( bChgFntColor )
        {
            if ( pFont && aNewColor != pFont->GetColor() )
            {
                // only set the new color at the font passed as argument
                pFont->SetColor( aNewColor );
            }
            else if ( aNewColor != GetOut().GetFont().GetColor() )
            {
                // set new font with new color at output device
                Font aFont( rFnt );
                aFont.SetColor( aNewColor );
                GetOut().SetFont( aFont );
            }
        }

        // the underline and overline colors have to be set separately
        if ( bChgLineColor )
        {
            // get current font color or color set at output device
            aNewColor = pFont ? pFont->GetColor() : GetOut().GetFont().GetColor();
            if ( aNewColor != GetOut().GetLineColor() )
                GetOut().SetLineColor( aNewColor );
            if ( aNewColor != GetOut().GetOverlineColor() )
                GetOut().SetOverlineColor( aNewColor );
        }

        return sal_True;
	}

    return sal_False;
}