#include "ASCOutputDevice.h"
#include "tools/bigint.hxx"
#include "vcl/salbtype.hxx"
#include "vcl/alpha.hxx"
#include "implncvt.hxx"
#include "vcl/wall2.hxx"
#include "vcl/bmpacc.hxx"
#include "vcl/impbmp.hxx"
#include "vcl/salgtype.hxx"
#include "vcl/salgdi.hxx"

#include <math.h>
#include "outmap.cxx"



using namespace SVMCore;

namespace SVMCore{

#define TEXT_DRAW_ELLIPSIS  (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS)

// =======================================================================

#define UNDERLINE_LAST      UNDERLINE_BOLDWAVE
#define STRIKEOUT_LAST      STRIKEOUT_X


#define OUTDEV_POLYPOLY_STACKBUF		32

// =======================================================================

static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY,
                           int nOrientation )
{
    if ( (nOrientation >= 0) && !(nOrientation % 900) )
    {
        if ( (nOrientation >= 3600) )
            nOrientation %= 3600;

        if ( nOrientation )
        {
            rX -= nOriginX;
            rY -= nOriginY;

            if ( nOrientation == 900 )
            {
                long nTemp = rX;
                rX = rY;
                rY = -nTemp;
            }
            else if ( nOrientation == 1800 )
            {
                rX = -rX;
                rY = -rY;
            }
            else /* ( nOrientation == 2700 ) */
            {
                long nTemp = rX;
                rX = -rY;
                rY = nTemp;
            }

            rX += nOriginX;
            rY += nOriginY;
        }
    }
    else
    {
        double nRealOrientation = nOrientation*F_PI1800;
        double nCos = cos( nRealOrientation );
        double nSin = sin( nRealOrientation );

        // Translation...
        long nX = rX-nOriginX;
        long nY = rY-nOriginY;

        // Rotation...
        rX = +((long)(nCos*nX + nSin*nY)) + nOriginX;
        rY = -((long)(nSin*nX - nCos*nY)) + nOriginY;
    }
}

// =======================================================================

struct ImplObjStack
{
    ImplObjStack*   mpPrev;
    MapMode*        mpMapMode;
    Region*         mpClipRegion;
    Color*          mpLineColor;
    Color*          mpFillColor;
    Font*           mpFont;
    Color*          mpTextColor;
    Color*          mpTextFillColor;
    Color*          mpTextLineColor;
    Color*          mpOverlineColor;
    Point*          mpRefPoint;
    TextAlign       meTextAlign;
    RasterOp        meRasterOp;
    ULONG           mnTextLayoutMode;
    LanguageType    meTextLanguage;
    USHORT          mnFlags;
	ImplObjStack()
	{
		mpPrev = NULL;
		mpMapMode = NULL;
		mpClipRegion = NULL;
		mpLineColor = NULL;
		mpFillColor = NULL;
		mpFont = NULL;
		mpTextColor = NULL;
		mpTextFillColor = NULL;
		mpTextLineColor = NULL;
		mpOverlineColor = NULL;
		mpRefPoint = NULL;
	}
};

// -----------------------------------------------------------------------

static void ImplDeleteObjStack( ImplObjStack* pObjStack )
{
	if ( pObjStack->mnFlags & PUSH_LINECOLOR )
	{
		if ( pObjStack->mpLineColor )
			delete pObjStack->mpLineColor;
	}
	if ( pObjStack->mnFlags & PUSH_FILLCOLOR )
	{
		if ( pObjStack->mpFillColor )
			delete pObjStack->mpFillColor;
	}
	if ( pObjStack->mnFlags & PUSH_FONT )
		delete pObjStack->mpFont;
	if ( pObjStack->mnFlags & PUSH_TEXTCOLOR )
		delete pObjStack->mpTextColor;
	if ( pObjStack->mnFlags & PUSH_TEXTFILLCOLOR )
	{
		if ( pObjStack->mpTextFillColor )
			delete pObjStack->mpTextFillColor;
	}
	if ( pObjStack->mnFlags & PUSH_TEXTLINECOLOR )
	{
		if ( pObjStack->mpTextLineColor )
			delete pObjStack->mpTextLineColor;
	}
	if ( pObjStack->mnFlags & PUSH_OVERLINECOLOR )
	{
		if ( pObjStack->mpOverlineColor )
			delete pObjStack->mpOverlineColor;
	}
	if ( pObjStack->mnFlags & PUSH_MAPMODE )
	{
		if ( pObjStack->mpMapMode )
			delete pObjStack->mpMapMode;
	}
	if ( pObjStack->mnFlags & PUSH_CLIPREGION )
	{
		if ( pObjStack->mpClipRegion )
			delete pObjStack->mpClipRegion;
	}
	if ( pObjStack->mnFlags & PUSH_REFPOINT )
	{
		if ( pObjStack->mpRefPoint )
			delete pObjStack->mpRefPoint;
	}

	delete pObjStack;
}

inline UINT8 ImplGetGradientColorValue( long nValue )
{
	if ( nValue < 0 )
		return 0;
	else if ( nValue > 0xFF )
		return 0xFF;
	else
		return (UINT8)nValue;
}

// ----------------
// - Cmp-Function -
// ----------------

int ImplHatchCmpFnc( const void* p1, const void* p2 )
{
	const long nX1 = ( (Point*) p1 )->X();
	const long nX2 = ( (Point*) p2 )->X();
	const long nY1 = ( (Point*) p1 )->Y();
	const long nY2 = ( (Point*) p2 )->Y();

	return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
}

// =======================================================================
ULONG ImplAdjustTwoRect( SalTwoRect& rTwoRect, const Size& rSizePix )
{
	ULONG nMirrFlags = 0;

	if ( rTwoRect.mnDestWidth < 0 )
	{
		rTwoRect.mnSrcX = rSizePix.Width() - rTwoRect.mnSrcX - rTwoRect.mnSrcWidth;
		rTwoRect.mnDestWidth = -rTwoRect.mnDestWidth;
		rTwoRect.mnDestX -= rTwoRect.mnDestWidth-1;
		nMirrFlags |= BMP_MIRROR_HORZ;
	}

	if ( rTwoRect.mnDestHeight < 0 )
	{
		rTwoRect.mnSrcY = rSizePix.Height() - rTwoRect.mnSrcY - rTwoRect.mnSrcHeight;
		rTwoRect.mnDestHeight = -rTwoRect.mnDestHeight;
		rTwoRect.mnDestY -= rTwoRect.mnDestHeight-1;
		nMirrFlags |= BMP_MIRROR_VERT;
	}

	if( ( rTwoRect.mnSrcX < 0 ) || ( rTwoRect.mnSrcX >= rSizePix.Width() ) ||
		( rTwoRect.mnSrcY < 0 ) || ( rTwoRect.mnSrcY >= rSizePix.Height() ) ||	
		( ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) > rSizePix.Width() ) ||
		( ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) > rSizePix.Height() ) )
	{
		const Rectangle	aSourceRect( Point( rTwoRect.mnSrcX, rTwoRect.mnSrcY ), 
									 Size( rTwoRect.mnSrcWidth, rTwoRect.mnSrcHeight ) );
		Rectangle		aCropRect( aSourceRect );

		aCropRect.Intersection( Rectangle( Point(), rSizePix ) );

		if( aCropRect.IsEmpty() )
			rTwoRect.mnSrcWidth = rTwoRect.mnSrcHeight = rTwoRect.mnDestWidth = rTwoRect.mnDestHeight = 0;
		else
		{
			const double	fFactorX = ( rTwoRect.mnSrcWidth > 1 ) ? (double) ( rTwoRect.mnDestWidth - 1 ) / ( rTwoRect.mnSrcWidth - 1 ) : 0.0;
			const double	fFactorY = ( rTwoRect.mnSrcHeight > 1 ) ? (double) ( rTwoRect.mnDestHeight - 1 ) / ( rTwoRect.mnSrcHeight - 1 ) : 0.0;

			const long nDstX1 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Left() - rTwoRect.mnSrcX ) );
			const long nDstY1 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Top() - rTwoRect.mnSrcY ) );
			const long nDstX2 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Right() - rTwoRect.mnSrcX ) );
			const long nDstY2 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Bottom() - rTwoRect.mnSrcY ) );

			rTwoRect.mnSrcX = aCropRect.Left();
			rTwoRect.mnSrcY = aCropRect.Top();
			rTwoRect.mnSrcWidth = aCropRect.GetWidth();
			rTwoRect.mnSrcHeight = aCropRect.GetHeight();
			rTwoRect.mnDestX = nDstX1;
			rTwoRect.mnDestY = nDstY1;
			rTwoRect.mnDestWidth = nDstX2 - nDstX1 + 1;
			rTwoRect.mnDestHeight = nDstY2 - nDstY1 + 1;
		}
	}

	return nMirrFlags;
}


// -----------------------------------------------------------------------

#define OUTDEV_INIT()						\
{											\
	if ( !mpGraphics )						\
		return; 							\
											\
	if ( mbInitClipRegion ) 				\
		ImplInitClipRegion();				\
											\
	if ( mbOutputClipped )					\
		return; 							\
}
// -----------
// - Defines -
// -----------

#define HATCH_MAXPOINTS				1024
#define GRADIENT_DEFAULT_STEPCOUNT	0


// -------------
// - externals -
// -------------

extern ULONG nVCLRLut[ 6 ];
extern ULONG nVCLGLut[ 6 ];
extern ULONG nVCLBLut[ 6 ];
extern ULONG nVCLDitherLut[ 256 ];
extern ULONG nVCLLut[ 256 ];

}//SVMCore

//int OutputDevice::ImplGetGraphics()
//{
//	if ( mpGraphics )
//		return TRUE;
//
//	mbInitLineColor 	= TRUE;
//	mbInitFillColor 	= TRUE;
//	mbInitFont			= TRUE;
//	mbInitTextColor 	= TRUE;
//	mbInitClipRegion	= TRUE;
//
//	if ( meOutDevType == OUTDEV_VIRDEV )
//		;//mpGraphics = new WinSalGraphics();
//
//	if ( mpGraphics )
//	{
//		mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
//        //mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
//        return TRUE;
//	}
//	return FALSE;
//}

void VirtualDevice::ImplInitVirDev( long nDX, long nDY, USHORT nBitCount )
{
	if ( nDX < 1 )
		nDX = 1;

	if ( nDY < 1 )
		nDY = 1;

	mnBitCount		= ( nBitCount ? nBitCount : GetBitCount() );
	mnOutWidth		= nDX;
	mnOutHeight 	= nDY;
	//mbScreenComp	= TRUE;
    //mnAlphaDepth	= -1;

 	meOutDevType	= OUTDEV_VIRDEV;
	//mbDevOutput 	= TRUE;
	mpFontList		= new ImplDevFontList();
	mpFontCache 	= new ImplFontCache( false );
	mpGraphics = GetWinGraphics();
	//mnDPIX			= pOutDev->mnDPIX;
	//mnDPIY			= pOutDev->mnDPIY;
	//maFont			= pOutDev->maFont;

	// Virtuelle Devices haben defaultmaessig einen weissen Hintergrund
	SetBackground( Wallpaper( Color( COL_WHITE ) ) );
}

OutputDevice::OutputDevice() :
	maRegion( REGION_NULL ),
	maFillColor( COL_WHITE ),
	maTextLineColor( COL_TRANSPARENT )//,
	//maSettings( Application::GetSettings() )
{
	mpGraphics			= NULL;
	//mpUnoGraphicsList	= NULL;
	//mpPrevGraphics		= NULL;
	//mpNextGraphics		= NULL;
	//mpMetaFile			= NULL;
	mpFontEntry 		= NULL;
	mpFontCache 		= NULL;
	mpFontList			= NULL;
	//mpGetDevFontList	= NULL;
	//mpGetDevSizeList	= NULL;
	mpObjStack			= NULL;
	//mpOutDevData		= NULL;
    //mpPDFWriter			= NULL;
    //mpAlphaVDev			= NULL;
	//mpExtOutDevData		= NULL;
	mnOutOffX			= 0;
	mnOutOffY			= 0;
	mnOutWidth			= 0;
	mnOutHeight 		= 0;
	mnDPIX				= 96;//todo
	mnDPIY				= 96;//todo
	mnTextOffX			= 0;
	mnTextOffY			= 0;
	mnOutOffOrigX		= 0;
	mnOutOffLogicX		= 0;
	mnOutOffOrigY		= 0;
	mnOutOffLogicY		= 0;
	mnEmphasisAscent	= 0;
	mnEmphasisDescent	= 0;
	//mnDrawMode			= 0;
	mnTextLayoutMode        = TEXT_LAYOUT_DEFAULT;
#ifdef AVS
	if( Application::GetSettings().GetLayoutRTL() ) //#i84553# tip BiDi preference to RTL
		mnTextLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT;
#endif
	meOutDevType		= OUTDEV_VIRDEV;
    //meOutDevViewType    = OUTDEV_VIEWTYPE_DONTKNOW;
	mbMap				= FALSE;
	//mbMapIsDefault		= TRUE;
	mbClipRegion		= FALSE;
	mbBackground		= FALSE;
	//mbOutput			= TRUE;
	//mbDevOutput 		= FALSE;
	mbOutputClipped 	= FALSE;
	maTextColor 		= Color( COL_BLACK );
	maOverlineColor 	= Color( COL_TRANSPARENT );
	//meTextAlign 		= maFont.GetAlign();
	meRasterOp			= ROP_OVERPAINT;
	mnAntialiasing		= 0;
    meTextLanguage      = 0;  // TODO: get default from configuration?
	mbLineColor 		= TRUE;
	mbFillColor 		= TRUE;
	mbInitLineColor 	= TRUE;
	mbInitFillColor 	= TRUE;
	mbInitFont			= TRUE;
	mbInitTextColor 	= TRUE;
	mbInitClipRegion	= TRUE;
	mbClipRegionSet 	= FALSE;
	mbKerning			= FALSE;
	mbNewFont			= TRUE;
	mbTextLines 		= FALSE;
	mbTextSpecial		= FALSE;
	mbRefPoint			= FALSE;
    mbEnableRTL         = FALSE;    // mirroring must be explicitly allowed (typically for windows only)

	// struct ImplMapRes
    maMapRes.mnMapOfsX 			= 0;
    maMapRes.mnMapOfsY 			= 0;
    maMapRes.mnMapScNumX 		= 1;
    maMapRes.mnMapScNumY 		= 1;
    maMapRes.mnMapScDenomX 		= 1;
    maMapRes.mnMapScDenomY		= 1;
	// struct ImplThresholdRes
    maThresRes.mnThresLogToPixX	= 0;
    maThresRes.mnThresLogToPixY	= 0;
    maThresRes.mnThresPixToLogX	= 0;
    maThresRes.mnThresPixToLogY	= 0;

	ImplInitVirDev( 1, 1, 0 );
}

// -----------------------------------------------------------------------

OutputDevice::~OutputDevice()
{
#ifdef AVS
	if ( GetUnoGraphicsList() )
	{
		UnoWrapperBase* pWrapper = Application::GetUnoWrapper( FALSE );
		if ( pWrapper )
			pWrapper->ReleaseAllGraphics( this );
		delete mpUnoGraphicsList;
		mpUnoGraphicsList = NULL;
	}
	if ( mpOutDevData )
		ImplDeInitOutDevData();
#endif

	ImplObjStack* pData = mpObjStack;
	if ( pData )
	{
		while ( pData )
		{
			ImplObjStack* pTemp = pData;
			pData = pData->mpPrev;
			ImplDeleteObjStack( pTemp );
		}
	}
#ifdef AVS
    // release the active font instance
    if( mpFontEntry )
        mpFontCache->Release( mpFontEntry );

    // remove cached results of GetDevFontList/GetDevSizeList
    // TODO: use smart pointers for them
    if( mpGetDevFontList )
        delete mpGetDevFontList;
    if( mpGetDevSizeList )
        delete mpGetDevSizeList;

    // release ImplFontCache specific to this OutputDevice
    // TODO: refcount ImplFontCache
    if( mpFontCache
    && (mpFontCache != ImplGetSVData()->maGDIData.mpScreenFontCache)
    && (ImplGetSVData()->maGDIData.mpScreenFontCache != NULL) )
    {
        delete mpFontCache;
        mpFontCache = NULL;
    }

    // release ImplFontList specific to this OutputDevice
    // TODO: refcount ImplFontList
    if( mpFontList
    && (mpFontList != ImplGetSVData()->maGDIData.mpScreenFontList)
    && (ImplGetSVData()->maGDIData.mpScreenFontList != NULL) )
    {
        mpFontList->Clear();
        delete mpFontList;
        mpFontList = NULL;
    }
    delete mpAlphaVDev;
#endif
#ifndef AVS_INSERT
	delete mpFontList;
	delete mpFontCache;
	delete mpGraphics;
#endif
}



void OutputDevice::ImplInitClipRegion()
{
	if ( mbClipRegion )
	{
		if ( maRegion.IsEmpty() )
			mbOutputClipped = TRUE;
		else
		{
			mbOutputClipped = FALSE;
			ImplSelectClipRegion(
                                  // #102532# Respect output offset also for clip region
                                  ImplPixelToDevicePixel( maRegion ) );
		}

		mbClipRegionSet = TRUE;
	}
	else
	{
		if ( mbClipRegionSet )
		{
			mpGraphics->ResetClipRegion();
			mbClipRegionSet = FALSE;
		}

		mbOutputClipped = FALSE;
	}
	mbInitClipRegion = FALSE;
}
bool OutputDevice::ImplSelectClipRegion( const Region& rRegion, SalGraphics* pGraphics )
{
    // TODO(Q3): Change from static to plain method - everybody's
    // calling it with pOutDev=this!
    // => done, but only with minimal changes for now => TODO
	OutputDevice* const pOutDev = this;
	if( !pGraphics )
	{
		if( !mpGraphics )
				return false;
		pGraphics = mpGraphics;
	}

	long				nX;
	long				nY;
	long				nWidth;
	long				nHeight;
	ULONG				nRectCount;
	ImplRegionInfo		aInfo;
	BOOL				bRegionRect;
	BOOL				bClipRegion = TRUE;
    const BOOL          bClipDeviceBounds = TRUE;//( !pOutDev->GetPDFWriter() && pOutDev->GetOutDevType() != OUTDEV_PRINTER );

	nRectCount = rRegion.GetRectCount();
	pGraphics->BeginSetClipRegion( nRectCount );
	bRegionRect = rRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight );
    if( bClipDeviceBounds )
    {
        // #b6520266# Perform actual rect clip against outdev
        // dimensions, to generate empty clips whenever one of the
        // values is completely off the device.
        const long nOffX( pOutDev->mnOutOffX );
        const long nOffY( pOutDev->mnOutOffY );
        const long nDeviceWidth( pOutDev->GetOutputWidthPixel() );
        const long nDeviceHeight( pOutDev->GetOutputHeightPixel() );
        Rectangle aDeviceBounds( nOffX, nOffY,
                                 nOffX+nDeviceWidth-1,
                                 nOffY+nDeviceHeight-1 );
        while ( bRegionRect )
        {
            // #i59315# Limit coordinates passed to sal layer to actual
            // outdev dimensions - everything else bears the risk of
            // overflowing internal coordinates (e.g. the 16 bit wire
            // format of X11).
            Rectangle aTmpRect(nX,nY,nX+nWidth-1,nY+nHeight-1);
            aTmpRect.Intersection(aDeviceBounds);

            if( !aTmpRect.IsEmpty() )
            {
                if ( !pGraphics->UnionClipRegion( aTmpRect.Left(),
                                                  aTmpRect.Top(),
                                                  aTmpRect.GetWidth(),
                                                  aTmpRect.GetHeight(),
                                                  pOutDev ) )
                {
                    bClipRegion = FALSE;
                }
            }
            else
            {
                //#i79850# Fake off-screen clip
                if ( !pGraphics->UnionClipRegion( nDeviceWidth+1,
                                                  nDeviceHeight+1,
                                                  1, 1,
                                                  pOutDev ) )
                {
                    bClipRegion = FALSE;
                }
            }
            //DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't create region" );
            bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
        }
    }
    else
    {
        // #i65720# Actually, _don't_ clip anything on printer or PDF
        // export, since output might be visible outside the specified
        // device boundaries.
        while ( bRegionRect )
        {
            if ( !pGraphics->UnionClipRegion( nX, nY, nWidth, nHeight, pOutDev ) )
                bClipRegion = FALSE;
            //DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't cerate region" );
            bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
        }
    }
	pGraphics->EndSetClipRegion();
	return bClipRegion;
}

void OutputDevice::ImplInitLineColor()
{
	//DBG_TESTSOLARMUTEX();

	if( mbLineColor )
	{
		if( ROP_0 == meRasterOp )
			mpGraphics->SetROPLineColor( SAL_ROP_0 );
		else if( ROP_1 == meRasterOp )
			mpGraphics->SetROPLineColor( SAL_ROP_1 );
		else if( ROP_INVERT == meRasterOp )
			mpGraphics->SetROPLineColor( SAL_ROP_INVERT );
		else
			mpGraphics->SetLineColor( ImplColorToSal( maLineColor ) );
	}
	else
		mpGraphics->SetLineColor();

	mbInitLineColor = FALSE;
}
void OutputDevice::ImplInitFillColor()
{
	if( mbFillColor )
	{
		if( ROP_0 == meRasterOp )
			mpGraphics->SetROPFillColor( SAL_ROP_0 );
		else if( ROP_1 == meRasterOp )
			mpGraphics->SetROPFillColor( SAL_ROP_1 );
		else if( ROP_INVERT == meRasterOp )
			mpGraphics->SetROPFillColor( SAL_ROP_INVERT );
		else
			mpGraphics->SetFillColor( ImplColorToSal( maFillColor ) );
	}
	else
		mpGraphics->SetFillColor();

	mbInitFillColor = FALSE;
}


Polygon OutputDevice::ImplSubdivideBezier( const Polygon& rPoly )
{
    Polygon aPoly;

    // #100127# Use adaptive subdivide instead of fixed 25 segments
    rPoly.AdaptiveSubdivide( aPoly );

    return aPoly;
}
// =======================================================================

PolyPolygon OutputDevice::ImplSubdivideBezier( const PolyPolygon& rPolyPoly )
{
    USHORT i, nPolys = rPolyPoly.Count();
    PolyPolygon aPolyPoly( nPolys );
    for( i=0; i<nPolys; ++i )
        aPolyPoly.Insert( ImplSubdivideBezier( rPolyPoly.GetObject(i) ) );

    return aPolyPoly;
}

void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
{
	Color aColor( rColor );

	Point aPt = ImplLogicToDevicePixel( rPt );

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( rColor ), this );
}
void OutputDevice::DrawPixel( const Point& rPt )
{
	Point aPt = ImplLogicToDevicePixel( rPt );

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	mpGraphics->DrawPixel( aPt.X(), aPt.Y(), this );
}
void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
{
	if ( !mbLineColor )
		return;

	if ( !mpGraphics )
			return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	Point aStartPt = ImplLogicToDevicePixel( rStartPt );
	Point aEndPt = ImplLogicToDevicePixel( rEndPt );

	mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
}
void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt, const LineInfo& rLineInfo )
{
	if ( rLineInfo.IsDefault() )
	{
		DrawLine( rStartPt, rEndPt );
		return;
	}

	if ( !mbLineColor || ( LINE_NONE == rLineInfo.GetStyle() ) )
		return;

	if( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();

	if ( mbOutputClipped )
		return;

	const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );

	if( ( aInfo.GetWidth() > 1L ) || ( LINE_DASH == aInfo.GetStyle() ) )
	{
		Polygon 			aPoly( 2 ); aPoly[ 0 ] = rStartPt; aPoly[ 1 ] = rEndPt;
		ImplLineConverter	aLineCvt( ImplLogicToDevicePixel( aPoly ), aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );

		if ( aInfo.GetWidth() > 1 )
		{
			const Color 	aOldLineColor( maLineColor );
			const Color 	aOldFillColor( maFillColor );

			SetLineColor();
			ImplInitLineColor();
			SetFillColor( aOldLineColor );
			ImplInitFillColor();

			for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
				mpGraphics->DrawPolygon( pPoly->GetSize(), (const SalPoint*) pPoly->GetConstPointAry(), this );

			SetFillColor( aOldFillColor );
			SetLineColor( aOldLineColor );
		}
		else
		{
			if ( mbInitLineColor )
				ImplInitLineColor();

			for ( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
				mpGraphics->DrawLine( (*pPoly)[ 0 ].X(), (*pPoly)[ 0 ].Y(), (*pPoly)[ 1 ].X(), (*pPoly)[ 1 ].Y(), this );
		}
	}
	else
	{
		const Point aStartPt( ImplLogicToDevicePixel( rStartPt ) );
		const Point aEndPt( ImplLogicToDevicePixel( rEndPt ) );

		if ( mbInitLineColor )
			ImplInitLineColor();

		mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
	}
}
void OutputDevice::DrawRect( const Rectangle& rRect )
{
	if ( (!mbLineColor && !mbFillColor) )
		return;

	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );

	if ( aRect.IsEmpty() )
		return;
	aRect.Justify();

	if ( !mpGraphics )
			return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();
	if ( mbInitFillColor )
		ImplInitFillColor();

	mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this );
}
void OutputDevice::DrawRect( const Rectangle& rRect, ULONG nHorzRound, ULONG nVertRound )
{
	if ( !mbLineColor && !mbFillColor )
		return;

	const Rectangle aRect( ImplLogicToDevicePixel( rRect ) );

	if ( aRect.IsEmpty() )
		return;

	nHorzRound = ImplLogicWidthToDevicePixel( nHorzRound );
	nVertRound = ImplLogicHeightToDevicePixel( nVertRound );

	// we need a graphics
	if ( !mpGraphics )
			return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();
	if ( mbInitFillColor )
		ImplInitFillColor();

	if ( !nHorzRound && !nVertRound )
		mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this );
	else
	{
		const Polygon aRoundRectPoly( aRect, nHorzRound, nVertRound );

		if ( aRoundRectPoly.GetSize() >= 2 )
		{
			const SalPoint* pPtAry = (const SalPoint*)aRoundRectPoly.GetConstPointAry();
			
			if ( !mbFillColor )
				mpGraphics->DrawPolyLine( aRoundRectPoly.GetSize(), pPtAry, this );
			else
				mpGraphics->DrawPolygon( aRoundRectPoly.GetSize(), pPtAry, this );
		}
	}
}
void OutputDevice::DrawEllipse( const Rectangle& rRect )
{
	if	( !mbLineColor && !mbFillColor )
		return;

	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
	if ( aRect.IsEmpty() )
		return;

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	Polygon aRectPoly( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
	if ( aRectPoly.GetSize() >= 2 )
	{
		const SalPoint* pPtAry = (const SalPoint*)aRectPoly.GetConstPointAry();
		if ( !mbFillColor )
			mpGraphics->DrawPolyLine( aRectPoly.GetSize(), pPtAry, this );
		else
		{
			if ( mbInitFillColor )
				ImplInitFillColor();
			mpGraphics->DrawPolygon( aRectPoly.GetSize(), pPtAry, this );
		}
	}
}
void OutputDevice::DrawArc( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
	if ( !mbLineColor  )
		return;

	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
	if ( aRect.IsEmpty() )
		return;

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	const Point 	aStart( ImplLogicToDevicePixel( rStartPt ) );
	const Point 	aEnd( ImplLogicToDevicePixel( rEndPt ) );
	Polygon 		aArcPoly( aRect, aStart, aEnd, POLY_ARC );

	if ( aArcPoly.GetSize() >= 2 )
	{
		const SalPoint* pPtAry = (const SalPoint*)aArcPoly.GetConstPointAry();
		mpGraphics->DrawPolyLine( aArcPoly.GetSize(), pPtAry, this );
	}
}
void OutputDevice::DrawPie( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
	if ( !mbLineColor && !mbFillColor )
		return;

	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
	if ( aRect.IsEmpty() )
		return;

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	const Point 	aStart( ImplLogicToDevicePixel( rStartPt ) );
	const Point 	aEnd( ImplLogicToDevicePixel( rEndPt ) );
	Polygon 		aPiePoly( aRect, aStart, aEnd, POLY_PIE );

	if ( aPiePoly.GetSize() >= 2 )
	{
		const SalPoint* pPtAry = (const SalPoint*)aPiePoly.GetConstPointAry();
		if ( !mbFillColor )
			mpGraphics->DrawPolyLine( aPiePoly.GetSize(), pPtAry, this );
		else
		{
			if ( mbInitFillColor )
				ImplInitFillColor();
			mpGraphics->DrawPolygon( aPiePoly.GetSize(), pPtAry, this );
		}
	}
}
void OutputDevice::DrawChord( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
	if ( !mbLineColor && !mbFillColor )
		return;

	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
	if ( aRect.IsEmpty() )
		return;

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	const Point 	aStart( ImplLogicToDevicePixel( rStartPt ) );
	const Point 	aEnd( ImplLogicToDevicePixel( rEndPt ) );
	Polygon 		aChordPoly( aRect, aStart, aEnd, POLY_CHORD );

	if ( aChordPoly.GetSize() >= 2 )
	{
		const SalPoint* pPtAry = (const SalPoint*)aChordPoly.GetConstPointAry();
		if ( !mbFillColor )
			mpGraphics->DrawPolyLine( aChordPoly.GetSize(), pPtAry, this );
		else
		{
			if ( mbInitFillColor )
				ImplInitFillColor();
			mpGraphics->DrawPolygon( aChordPoly.GetSize(), pPtAry, this );
		}
	}
}

void OutputDevice::DrawPolyLine( const Polygon& rPoly )
{
	USHORT nPoints = rPoly.GetSize();
	if ( !mbLineColor || (nPoints < 2) )
		return;

	// we need a graphics
	if ( !mpGraphics )
			return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();

	Polygon aPoly = ImplLogicToDevicePixel( rPoly );
    const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();

    // #100127# Forward beziers to sal, if any
    if( aPoly.HasFlags() )
    {
        const BYTE* pFlgAry = aPoly.GetConstFlagAry();
        if( !mpGraphics->DrawPolyLineBezier( nPoints, pPtAry, pFlgAry, this ) )
        {
            aPoly = ImplSubdivideBezier(aPoly);
            pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
            mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, this );
        }
    }
    else
    {
        mpGraphics->DrawPolyLine( nPoints, pPtAry, this );
    }
}
void OutputDevice::DrawPolyLine( const Polygon& rPoly, const LineInfo& rLineInfo )
{
	if ( rLineInfo.IsDefault() )
	{
		DrawPolyLine( rPoly );
		return;
	}
#ifdef AVS
    // #i101491#
    // Try direct Fallback to B2D-Version of DrawPolyLine
    if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) 
        && LINE_SOLID == rLineInfo.GetStyle())
    {
		DrawPolyLine(rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), basegfx::B2DLINEJOIN_ROUND);
		return;
    }
#endif
    ImpDrawPolyLineWithLineInfo(rPoly, rLineInfo);
}
void OutputDevice::ImpDrawPolyLineWithLineInfo(const Polygon& rPoly, const LineInfo& rLineInfo)
{
	USHORT nPoints = rPoly.GetSize();

	if ( !mbLineColor || ( nPoints < 2 ) || ( LINE_NONE == rLineInfo.GetStyle() ) )
		return;

    Polygon aPoly = ImplLogicToDevicePixel( rPoly );

    // #100127# LineInfo is not curve-safe, subdivide always
    if( aPoly.HasFlags() )
    {
        aPoly = ImplSubdivideBezier( aPoly );
        nPoints = aPoly.GetSize();
    }

	// we need a graphics
	if ( !mpGraphics )
		return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();

	if ( mbOutputClipped )
		return;

	const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );

	if( aInfo.GetWidth() > 1L )
	{
		const Color 		aOldLineColor( maLineColor );
		const Color 		aOldFillColor( maFillColor );
		ImplLineConverter	aLineCvt( aPoly, aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );

		SetLineColor();
		ImplInitLineColor();
		SetFillColor( aOldLineColor );
		ImplInitFillColor();
        bool bDone(false);

        if(!bDone)
        {
            for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
            {
	    		mpGraphics->DrawPolygon( pPoly->GetSize(), (const SalPoint*) pPoly->GetConstPointAry(), this );
            }
        }

        SetLineColor( aOldLineColor );
		SetFillColor( aOldFillColor );
	}
	else
	{
		if ( mbInitLineColor )
			ImplInitLineColor();
		if ( LINE_DASH == aInfo.GetStyle() )
		{
			ImplLineConverter	aLineCvt( aPoly, aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );
			for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
				mpGraphics->DrawPolyLine( pPoly->GetSize(), (const SalPoint*)pPoly->GetConstPointAry(), this );
		}
		else
		mpGraphics->DrawPolyLine( nPoints, (const SalPoint*) aPoly.GetConstPointAry(), this );
	}
}

void OutputDevice::DrawPolygon( const Polygon& rPoly )
{
	USHORT nPoints = rPoly.GetSize();
	if ( (!mbLineColor && !mbFillColor) || (nPoints < 2) )
		return;

	// we need a graphics
	if ( !mpGraphics )
			return;
	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();
	if ( mbInitFillColor )
		ImplInitFillColor();

	Polygon aPoly = ImplLogicToDevicePixel( rPoly );
	const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();

    // #100127# Forward beziers to sal, if any
    if( aPoly.HasFlags() )
    {
        const BYTE* pFlgAry = aPoly.GetConstFlagAry();
        if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, this ) )
        {
            aPoly = ImplSubdivideBezier(aPoly);
            pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
            mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, this );
        }
    }
    else
    {
        mpGraphics->DrawPolygon( nPoints, pPtAry, this );
    }
}
// =======================================================================

void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly )
{
	if( pClipPolyPoly )
		ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
	else
	{
		USHORT nPoints = rPoly.GetSize();

		if ( nPoints < 2 )
			return;

		const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
		mpGraphics->DrawPolygon( nPoints, pPtAry, this );
	}
}
void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const PolyPolygon* pClipPolyPoly )
{
	PolyPolygon* pPolyPoly;

	if( pClipPolyPoly )
	{
		pPolyPoly = new PolyPolygon;
		rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
	}
	else
		pPolyPoly = (PolyPolygon*) &rPolyPoly;

	if( pPolyPoly->Count() == 1 )
	{
		const Polygon	rPoly = pPolyPoly->GetObject( 0 );
		USHORT			nSize = rPoly.GetSize();
		
		if( nSize >= 2 )
		{
			const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
			mpGraphics->DrawPolygon( nSize, pPtAry, this );
		}
	}
	else if( pPolyPoly->Count() )
	{
		USHORT				nCount = pPolyPoly->Count();
		sal_uInt32*			pPointAry = new sal_uInt32[nCount];
		PCONSTSALPOINT* 	pPointAryAry = new PCONSTSALPOINT[nCount];
		USHORT				i = 0;
		do
		{
			const Polygon&	rPoly = pPolyPoly->GetObject( i );
			USHORT			nSize = rPoly.GetSize();
			if ( nSize )
			{
				pPointAry[i]	= nSize;
				pPointAryAry[i] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
				i++;
			}
			else
				nCount--;
		}
		while( i < nCount );

		if( nCount == 1 )
			mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
		else
			mpGraphics->DrawPolyPolygon( nCount, pPointAry, pPointAryAry, this );
		
		delete[] pPointAry;
		delete[] pPointAryAry;
	}

	if( pClipPolyPoly )
		delete pPolyPoly;
}

void OutputDevice::DrawPolyPolygon( const PolyPolygon& rPolyPoly )
{
	USHORT nPoly = rPolyPoly.Count();

	if ( (!mbLineColor && !mbFillColor) || !nPoly )
		return;

	// we need a graphics
	if ( !mpGraphics )
			return;

	if ( mbInitClipRegion )
		ImplInitClipRegion();
	if ( mbOutputClipped )
		return;

	if ( mbInitLineColor )
		ImplInitLineColor();
	if ( mbInitFillColor )
		ImplInitFillColor();

	if ( nPoly == 1 )
	{
        // #100127# Map to DrawPolygon
        Polygon aPoly = rPolyPoly.GetObject( 0 );
		if( aPoly.GetSize() >= 2 )
			DrawPolygon( aPoly );
	}
	else
	{
        // #100127# moved real PolyPolygon draw to separate method,
        // have to call recursively, avoiding duplicate
        // ImplLogicToDevicePixel calls
        ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) );
	}
}
// #100127# Extracted from OutputDevice::DrawPolyPolygon()
void OutputDevice::ImplDrawPolyPolygon( USHORT nPoly, const PolyPolygon& rPolyPoly )
{
    // AW: This crashes on empty PolyPolygons, avoid that
    if(!nPoly)
        return;

    sal_uInt32			aStackAry1[OUTDEV_POLYPOLY_STACKBUF];
    PCONSTSALPOINT		aStackAry2[OUTDEV_POLYPOLY_STACKBUF];
    BYTE*				aStackAry3[OUTDEV_POLYPOLY_STACKBUF];
    sal_uInt32*			pPointAry;
    PCONSTSALPOINT* 	pPointAryAry;
    const BYTE**	 	pFlagAryAry;
    USHORT				i = 0, j = 0, last = 0;
    BOOL				bHaveBezier = sal_False;
    if ( nPoly > OUTDEV_POLYPOLY_STACKBUF )
    {
        pPointAry		= new sal_uInt32[nPoly];
        pPointAryAry	= new PCONSTSALPOINT[nPoly];
        pFlagAryAry		= new const BYTE*[nPoly];
    }
    else
    {
        pPointAry		= aStackAry1;
        pPointAryAry	= aStackAry2;
        pFlagAryAry		= (const BYTE**)aStackAry3;
    }
    do
    {
        const Polygon&	rPoly = rPolyPoly.GetObject( i );
        USHORT			nSize = rPoly.GetSize();
        if ( nSize )
        {
            pPointAry[j]	= nSize;
            pPointAryAry[j] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
            pFlagAryAry[j]  = rPoly.GetConstFlagAry();
            last 			= i;

            if( pFlagAryAry[j] )
                bHaveBezier = sal_True;

            ++j;
        }

        ++i;
    }
    while ( i < nPoly );

    if ( j == 1 )
    {
        // #100127# Forward beziers to sal, if any
        if( bHaveBezier )
        {
            if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, this ) )
            {
                Polygon aPoly = ImplSubdivideBezier( rPolyPoly.GetObject( last ) );
                mpGraphics->DrawPolygon( aPoly.GetSize(), (const SalPoint*)aPoly.GetConstPointAry(), this );
            }
        }
        else
        {
            mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
        }
    }
    else
    {
        // #100127# Forward beziers to sal, if any
        if( bHaveBezier )
        {
            if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) )
            {
                PolyPolygon aPolyPoly = ImplSubdivideBezier( rPolyPoly );
                ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly );
            }
        }
        else
        {
            mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this );
        }
    }

    if ( pPointAry != aStackAry1 )
    {
        delete[] pPointAry;
        delete[] pPointAryAry;
        delete[] pFlagAryAry;
    }
}

// -----------------------------------------------------------------------

void OutputDevice::ImplDrawText( const Rectangle& rRect,
                                 const String& rOrigStr, USHORT nStyle,
                                 MetricVector* pVector, String* pDisplayText )
{
    Color aOldTextColor;
    Color aOldTextFillColor;
    BOOL  bRestoreFillColor = false;
#ifdef AVS
    if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
    {
        BOOL  bHighContrastBlack = FALSE;
        BOOL  bHighContrastWhite = FALSE;
		Color aCol;
		if( IsBackground() )
			aCol = GetBackground().GetColor();
		else
			// best guess is the face color here
			// but it may be totally wrong. the background color
			// was typically already reset
			aCol = GetSettings().GetStyleSettings().GetFaceColor();

		bHighContrastBlack = aCol.IsDark();
		bHighContrastWhite = aCol.IsBright() && GetSettings().GetStyleSettings().GetHighContrastMode();

        aOldTextColor = GetTextColor();
        if ( IsTextFillColor() )
        {
            bRestoreFillColor = TRUE;
            aOldTextFillColor = GetTextFillColor();
        }
        else
            bRestoreFillColor = FALSE;
        if( bHighContrastBlack )
            SetTextColor( COL_GREEN );
        else if( bHighContrastWhite )
            SetTextColor( COL_LIGHTGREEN );
        else
        {
            // draw disabled text always without shadow
            // as it fits better with native look
            /*
            SetTextColor( GetSettings().GetStyleSettings().GetLightColor() );
            Rectangle aRect = rRect;
            aRect.Move( 1, 1 );
            DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE );
            */
            SetTextColor( GetSettings().GetStyleSettings().GetShadowColor() );
        }
    }
#endif
    long        nWidth          = rRect.GetWidth();
    long        nHeight         = rRect.GetHeight();

    if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
        return;

    Point       aPos            = rRect.TopLeft();

    long        nTextHeight     = GetTextHeight();
    TextAlign   eAlign          = GetTextAlign();
    xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;

    String aStr = rOrigStr;
    if ( nStyle & TEXT_DRAW_MNEMONIC )
        aStr = GetNonMnemonicString( aStr, nMnemonicPos );
#ifdef AVS
    // Mehrzeiligen Text behandeln wir anders
    if ( nStyle & TEXT_DRAW_MULTILINE )
    {

        XubString               aLastLine;
        ImplMultiTextLineInfo   aMultiLineInfo;
        ImplTextLineInfo*       pLineInfo;
        long                    nMaxTextWidth;
        xub_StrLen              i;
        xub_StrLen              nLines;
        xub_StrLen              nFormatLines;

        if ( nTextHeight )
        {
            nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
            nLines = (xub_StrLen)(nHeight/nTextHeight);
            nFormatLines = aMultiLineInfo.Count();
            if ( !nLines )
                nLines = 1;
            if ( nFormatLines > nLines )
            {
                if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
                {
                    // Letzte Zeile zusammenbauen und kuerzen
                    nFormatLines = nLines-1;

                    pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
                    aLastLine = aStr.Copy( pLineInfo->GetIndex() );
                    aLastLine.ConvertLineEnd( LINEEND_LF );
                    // Alle LineFeed's durch Spaces ersetzen
                    xub_StrLen nLastLineLen = aLastLine.Len();
                    for ( i = 0; i < nLastLineLen; i++ )
                    {
                        if ( aLastLine.GetChar( i ) == _LF )
                            aLastLine.SetChar( i, ' ' );
                    }
                    aLastLine = GetEllipsisString( aLastLine, nWidth, nStyle );
                    nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
                    nStyle |= TEXT_DRAW_TOP;
                }
            }
            else
            {
                if ( nMaxTextWidth <= nWidth )
                    nStyle &= ~TEXT_DRAW_CLIP;
            }

            // Muss in der Hoehe geclippt werden?
            if ( nFormatLines*nTextHeight > nHeight )
                nStyle |= TEXT_DRAW_CLIP;

            // Clipping setzen
            if ( nStyle & TEXT_DRAW_CLIP )
            {
                Push( PUSH_CLIPREGION );
                IntersectClipRegion( rRect );
            }

            // Vertikales Alignment
            if ( nStyle & TEXT_DRAW_BOTTOM )
                aPos.Y() += nHeight-(nFormatLines*nTextHeight);
            else if ( nStyle & TEXT_DRAW_VCENTER )
                aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;

            // Font Alignment
            if ( eAlign == ALIGN_BOTTOM )
                aPos.Y() += nTextHeight;
            else if ( eAlign == ALIGN_BASELINE )
                aPos.Y() += GetFontMetric().GetAscent();

            // Alle Zeilen ausgeben, bis auf die letzte
            for ( i = 0; i < nFormatLines; i++ )
            {
                pLineInfo = aMultiLineInfo.GetLine( i );
                if ( nStyle & TEXT_DRAW_RIGHT )
                    aPos.X() += nWidth-pLineInfo->GetWidth();
                else if ( nStyle & TEXT_DRAW_CENTER )
                    aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
                xub_StrLen nIndex   = pLineInfo->GetIndex();
                xub_StrLen nLineLen = pLineInfo->GetLen();
                DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
                if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
                {
                    if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
                    {
                        long        nMnemonicX;
                        long        nMnemonicY;
                        long        nMnemonicWidth;

                        sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
                        /*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray,
                                                nIndex, nLineLen);
                        long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
                        long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
                        nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));

                        Point       aTempPos = LogicToPixel( aPos );
                        nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) );
                        nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
                        ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
                    }
                }
                aPos.Y() += nTextHeight;
                aPos.X() = rRect.Left();
            }


            // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
            // da die Zeile gekuerzt wurde
            if ( aLastLine.Len() )
                DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText );

            // Clipping zuruecksetzen
            if ( nStyle & TEXT_DRAW_CLIP )
                Pop();
        }
    }
    else
#endif
    {
        long nTextWidth = GetTextWidth( aStr );
#ifdef AVS
        // Evt. Text kuerzen
        if ( nTextWidth > nWidth )
        {
            if ( nStyle & TEXT_DRAW_ELLIPSIS )
            {
                aStr = GetEllipsisString( aStr, nWidth, nStyle );
                nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
                nStyle |= TEXT_DRAW_LEFT;
                nTextWidth = GetTextWidth( aStr );
            }
        }
        else
        {
            if ( nTextHeight <= nHeight )
                nStyle &= ~TEXT_DRAW_CLIP;
        }

        // horizontal text alignment
        if ( nStyle & TEXT_DRAW_RIGHT )
            aPos.X() += nWidth-nTextWidth;
        else if ( nStyle & TEXT_DRAW_CENTER )
            aPos.X() += (nWidth-nTextWidth)/2;

        // vertical font alignment
        if ( eAlign == ALIGN_BOTTOM )
            aPos.Y() += nTextHeight;
        else if ( eAlign == ALIGN_BASELINE )
            aPos.Y() += GetFontMetric().GetAscent();

        if ( nStyle & TEXT_DRAW_BOTTOM )
            aPos.Y() += nHeight-nTextHeight;
        else if ( nStyle & TEXT_DRAW_VCENTER )
            aPos.Y() += (nHeight-nTextHeight)/2;

        long        nMnemonicX = 0;
        long        nMnemonicY = 0;
        long        nMnemonicWidth = 0;
        if ( nMnemonicPos != STRING_NOTFOUND )
        {
            sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() );
            /*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() );
            long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
            long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
            nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));

            Point aTempPos = LogicToPixel( aPos );
            nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) );
            nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
        }
#endif
        if ( nStyle & TEXT_DRAW_CLIP )
        {
            Push( PUSH_CLIPREGION );
            IntersectClipRegion( rRect );
            DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
#ifdef AVS
            if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
            {
                if ( nMnemonicPos != STRING_NOTFOUND )
                    ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
            }
#endif
            Pop();
        }
        else
        {
            DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
#ifdef AVS
            if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
            {
                if ( nMnemonicPos != STRING_NOTFOUND )
                    ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
            }
#endif
        }
    }

    if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
    {
        SetTextColor( aOldTextColor );
        if ( bRestoreFillColor )
            SetTextFillColor( aOldTextFillColor );
    }
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
                                     long nX, long nY, long nWidth, long nHeight )
{
    short nOrientation = mpFontEntry->mnOrientation;
    if ( nOrientation )
    {
        // Rotate rect without rounding problems for 90 degree rotations
        if ( !(nOrientation % 900) )
        {
            nX -= nBaseX;
            nY -= nBaseY;

            if ( nOrientation == 900 )
            {
                long nTemp = nX;
                nX = nY;
                nY = -nTemp;
                nTemp = nWidth;
                nWidth = nHeight;
                nHeight = nTemp;
                nY -= nHeight;
            }
            else if ( nOrientation == 1800 )
            {
                nX = -nX;
                nY = -nY;
                nX -= nWidth;
                nY -= nHeight;
            }
            else /* ( nOrientation == 2700 ) */
            {
                long nTemp = nX;
                nX = -nY;
                nY = nTemp;
                nTemp = nWidth;
                nWidth = nHeight;
                nHeight = nTemp;
                nX -= nWidth;
            }

            nX += nBaseX;
            nY += nBaseY;
        }
        else
        {
            // inflate because polygons are drawn smaller
            Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
            Polygon   aPoly( aRect );
            aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
            ImplDrawPolygon( aPoly );
            return;
        }
    }

    mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this );
}
// -----------------------------------------------------------------------

long OutputDevice::GetTextWidth( const String& rStr,
                                 xub_StrLen nIndex, xub_StrLen nLen ) const
{
    //DBG_TRACE( "OutputDevice::GetTextWidth()" );
    //DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    long nWidth = GetTextArray( rStr, NULL, nIndex, nLen );
    return nWidth;
}

// -----------------------------------------------------------------------

long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry,
                                 xub_StrLen nIndex, xub_StrLen nLen )  const
{
    //DBG_TRACE( "OutputDevice::GetTextArray()" );
    //DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    if( nIndex >= rStr.Len() )
        return 0;
    if( (ULONG)nIndex+nLen >= rStr.Len() )
        nLen = rStr.Len() - nIndex;

    // do layout
    SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
    if( !pSalLayout )
        return 0;

    long nWidth = pSalLayout->FillDXArray( pDXAry );
    int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    pSalLayout->Release();

    // convert virtual char widths to virtual absolute positions
    if( pDXAry )
        for( int i = 1; i < nLen; ++i )
            pDXAry[ i ] += pDXAry[ i-1 ];

    // convert from font units to logical units
    if( mbMap )
    {
        if( pDXAry )
            for( int i = 0; i < nLen; ++i )
                pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
        nWidth = ImplDevicePixelToLogicWidth( nWidth );
    }

    if( nWidthFactor > 1 )
    {
        if( pDXAry )
            for( int i = 0; i < nLen; ++i )
                pDXAry[i] /= nWidthFactor;
        nWidth /= nWidthFactor;
    }

    return nWidth;
}
long OutputDevice::GetTextHeight() const
{
    //DBG_TRACE( "OutputDevice::GetTextHeight()" );
    //DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    if( mbNewFont )
        if( !ImplNewFont() )
            return 0;

    if( mbInitFont )
		ImplInitFont();
#ifdef AVS
        if( !ImplNewFont() )
            return 0;
#endif

    long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;

    if ( mbMap )
        nHeight = ImplDevicePixelToLogicHeight( nHeight );

    return nHeight;
}

// -----------------------------------------------------------------------

String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos )
{
    String   aStr    = rStr;
    xub_StrLen  nLen    = aStr.Len();
    xub_StrLen  i       = 0;

    rMnemonicPos = STRING_NOTFOUND;
    while ( i < nLen )
    {
        if ( aStr.GetChar( i ) == '~' )
        {
            if ( aStr.GetChar( i+1 ) != '~' )
            {
                if ( rMnemonicPos == STRING_NOTFOUND )
                    rMnemonicPos = i;
                aStr.Erase( i, 1 );
                nLen--;
            }
            else
            {
                aStr.Erase( i, 1 );
                nLen--;
                i++;
            }
        }
        else
            i++;
    }

    return aStr;
}
void OutputDevice::DrawText( const Point& rStartPt, const XubString& rStr,
                                  xub_StrLen nIndex , xub_StrLen nLen ,
                                  MetricVector* pVector, String* pDisplayText )
{
    if( pVector )
    {
        Region aClip( GetClipRegion() );
        if( meOutDevType == OUTDEV_WINDOW )
            aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
        if( ! aClip.IsNull() )
        {
            MetricVector aTmp;
            GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );

            bool bInserted = false;
            for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
            {
                bool bAppend = false;

                if( aClip.IsOver( *it ) )
                    bAppend = true;
                else if( rStr.GetChar( nIndex ) == ' ' && bInserted )
                {
                    MetricVector::const_iterator next = it;
                    ++next;
                    if( next != aTmp.end() && aClip.IsOver( *next ) )
                        bAppend = true;
                }

                if( bAppend )
                {
                    pVector->push_back( *it );
                    if( pDisplayText )
                        pDisplayText->Append( rStr.GetChar( nIndex ) );
                    bInserted = true;
                }
            }
        }
        else
        {
            GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
            if( pDisplayText )
                pDisplayText->Append( rStr.Copy( nIndex, nLen ) );
        }
    }

    if ( pVector )
        return;

    SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true );
    if( pSalLayout )
    {
        ImplDrawText( *pSalLayout );
        pSalLayout->Release();
	}
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
{
    if( mbInitClipRegion )
        ImplInitClipRegion();
    if( mbOutputClipped )
        return;
    if( mbInitTextColor )
        ImplInitTextColor();

    rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );

    if( IsTextFillColor() )
        ImplDrawTextBackground( rSalLayout );

    if( mbTextSpecial )
        ImplDrawSpecialText( rSalLayout );
    else
        ImplDrawTextDirect( rSalLayout, mbTextLines );
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
{
    const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
    const Point aBase = rSalLayout.DrawBase();
    const long nX = aBase.X();
    const long nY = aBase.Y();

    if ( mbLineColor || mbInitLineColor )
    {
        mpGraphics->SetLineColor();
        mbInitLineColor = TRUE;
    }
    mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
    mbInitFillColor = TRUE;

    ImplDrawTextRect( nX, nY, nX, nY-mpFontEntry->maMetric.mnAscent-mnEmphasisAscent,
                      nWidth,
                      mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, BOOL bTextLines )
{

    if( mpFontEntry->mnOwnOrientation )
        if( ImplDrawRotateText( rSalLayout ) )
            return;

    long nOldX = rSalLayout.DrawBase().X();
#ifdef AVS
    if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
    {

        if( ImplHasMirroredGraphics() )
        {
            long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
            long x = rSalLayout.DrawBase().X();
       		rSalLayout.DrawBase().X() = w - 1 - x;
            if( !IsRTLEnabled() )
            {
                OutputDevice *pOutDevRef = (OutputDevice *)this;
                // mirror this window back
                long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
                rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
            }
        }
        else if( IsRTLEnabled() )
        {
            //long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
            //long x = rSalLayout.DrawBase().X();
            OutputDevice *pOutDevRef = (OutputDevice *)this;
            // mirror this window back
            long devX = pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
            rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
        }
#endif
        rSalLayout.DrawText( *mpGraphics );
#ifdef AVS
    }
#endif
    rSalLayout.DrawBase().X() = nOldX;

    if( bTextLines )
        ImplDrawTextLines( rSalLayout,
            maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
            maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
#ifdef QWE
    // emphasis marks
    if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
        ImplDrawEmphasisMarks( rSalLayout );
#endif

}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
{
#ifdef QWE
    Color               aOldColor       = GetTextColor();
    Color               aOldLineColor   = GetLineColor();
    Color               aOldFillColor   = GetFillColor();
    BOOL                bOldMap         = mbMap;
    //GDIMetaFile*        pOldMetaFile    = mpMetaFile;
    //mpMetaFile = NULL;
    EnableMapMode( FALSE );

    FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
    PolyPolygon         aPolyPoly;
    Rectangle           aRect1;
    Rectangle           aRect2;
    long                nEmphasisYOff;
    long                nEmphasisWidth;
    long                nEmphasisHeight;
    BOOL                bPolyLine;

    if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
        nEmphasisHeight = mnEmphasisDescent;
    else
        nEmphasisHeight = mnEmphasisAscent;

    ImplGetEmphasisMark( aPolyPoly, bPolyLine,
                         aRect1, aRect2,
                         nEmphasisYOff, nEmphasisWidth,
                         nEmphasisMark,
                         nEmphasisHeight, mpFontEntry->mnOrientation );

    if ( bPolyLine )
    {
        SetLineColor( GetTextColor() );
        SetFillColor();
    }
    else
    {
        SetLineColor();
        SetFillColor( GetTextColor() );
    }

    Point aOffset = Point(0,0);

    if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
        aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff;
    else
        aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff;

    long nEmphasisWidth2  = nEmphasisWidth / 2;
    long nEmphasisHeight2 = nEmphasisHeight / 2;
    aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );

    Point aOutPoint;
    Rectangle aRectangle;
    for( int nStart = 0;;)
    {
        sal_GlyphId nGlyphIndex;
        if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aOutPoint, nStart ) )
            break;

        if( !mpGraphics->GetGlyphBoundRect( nGlyphIndex, aRectangle ) )
            continue;

        if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
        {
            Point aAdjPoint = aOffset;
            aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
            if ( mpFontEntry->mnOrientation )
                ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation );
            aOutPoint += aAdjPoint;
            aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
            ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
                                  aOutPoint.X(), aOutPoint.Y(),
                                  aPolyPoly, bPolyLine, aRect1, aRect2 );
        }
    }

    SetLineColor( aOldLineColor );
    SetFillColor( aOldFillColor );
    EnableMapMode( bOldMap );
    //mpMetaFile = pOldMetaFile;
#endif
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout,
    FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, BOOL bWordLine, BOOL bUnderlineAbove )
{
    if( bWordLine )
    {
        Point aPos, aStartPt;
        sal_Int32 nWidth = 0, nAdvance=0;
        for( int nStart = 0;;)
        {
            sal_GlyphId nGlyphIndex;
            if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
                break;

            if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
            {
                if( !nWidth )
                {
                    aStartPt = aPos;//rSalLayout.DrawBase() - (aPos - rSalLayout.DrawOffset());
                }

                nWidth += nAdvance;
            }
            else if( nWidth > 0 )
            {
                ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
                    eStrikeout, eUnderline, eOverline, bUnderlineAbove );
                nWidth = 0;
            }
        }

        if( nWidth > 0 )
        {
            ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
                eStrikeout, eUnderline, eOverline, bUnderlineAbove );
        }
    }
    else
    {
        Point aStartPt = rSalLayout.GetDrawPosition();
        int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
        ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
            eStrikeout, eUnderline, eOverline, bUnderlineAbove );
    }
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawTextLine( long nBaseX,
                                     long nX, long nY, long nWidth,
                                     FontStrikeout eStrikeout,
                                     FontUnderline eUnderline,
                                     FontUnderline eOverline,
                                     BOOL bUnderlineAbove )
{
    if ( !nWidth )
        return;

    Color           aStrikeoutColor = GetTextColor();
    Color           aUnderlineColor = GetTextLineColor();
    Color           aOverlineColor  = GetOverlineColor();
    BOOL            bStrikeoutDone = FALSE;
    BOOL            bUnderlineDone = FALSE;
    BOOL            bOverlineDone  = FALSE;

    // TODO: fix rotated text
    if ( IsRTLEnabled() )
        // --- RTL --- mirror at basex
        nX = nBaseX - nWidth - (nX - nBaseX - 1);

    if ( !IsTextLineColor() )
        aUnderlineColor = GetTextColor();

    if ( !IsOverlineColor() )
        aOverlineColor = GetTextColor();

    if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
         (eUnderline == UNDERLINE_WAVE) ||
         (eUnderline == UNDERLINE_DOUBLEWAVE) ||
         (eUnderline == UNDERLINE_BOLDWAVE) )
    {
        ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
        bUnderlineDone = TRUE;
    }
    if ( (eOverline == UNDERLINE_SMALLWAVE) ||
         (eOverline == UNDERLINE_WAVE) ||
         (eOverline == UNDERLINE_DOUBLEWAVE) ||
         (eOverline == UNDERLINE_BOLDWAVE) )
    {
        ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );
        bOverlineDone = TRUE;
    }

    if ( (eStrikeout == STRIKEOUT_SLASH) ||
         (eStrikeout == STRIKEOUT_X) )
    {
        ImplDrawStrikeoutChar( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
        bStrikeoutDone = TRUE;
    }

    if ( !bUnderlineDone )
        ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );

    if ( !bOverlineDone )
        ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );

    if ( !bStrikeoutDone )
        ImplDrawStrikeoutLine( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
                                             long nX, long nY, long nWidth,
                                             FontUnderline eTextLine,
                                             Color aColor,
                                             BOOL bIsAbove )
{
    ImplFontEntry*  pFontEntry = mpFontEntry;
    long            nLineHeight = 0;
    long            nLinePos  = 0;
    long            nLinePos2 = 0;

    if ( eTextLine > UNDERLINE_LAST )
        eTextLine = UNDERLINE_SINGLE;

    switch ( eTextLine )
    {
        case UNDERLINE_SINGLE:
        case UNDERLINE_DOTTED:
        case UNDERLINE_DASH:
        case UNDERLINE_LONGDASH:
        case UNDERLINE_DASHDOT:
        case UNDERLINE_DASHDOTDOT:
            if ( bIsAbove )
            {
                nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
            }
            else
            {
                nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnUnderlineOffset;
            }
            break;
        case UNDERLINE_BOLD:
        case UNDERLINE_BOLDDOTTED:
        case UNDERLINE_BOLDDASH:
        case UNDERLINE_BOLDLONGDASH:
        case UNDERLINE_BOLDDASHDOT:
        case UNDERLINE_BOLDDASHDOTDOT:
            if ( bIsAbove )
            {
                nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
            }
            else
            {
                nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnBUnderlineOffset;
            }
            break;
        case UNDERLINE_DOUBLE:
            if ( bIsAbove )
            {
                nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
                nLinePos2   = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
            }
            else
            {
                nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
                nLinePos    = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
                nLinePos2   = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
            }
            break;
        default:
            break;
    }

    if ( nLineHeight )
    {
        if ( mbLineColor || mbInitLineColor )
        {
            mpGraphics->SetLineColor();
            mbInitLineColor = TRUE;
        }
        mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
        mbInitFillColor = TRUE;

        long nLeft = nX;

        switch ( eTextLine )
        {
            case UNDERLINE_SINGLE:
            case UNDERLINE_BOLD:
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
                break;
            case UNDERLINE_DOUBLE:
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos,  nWidth, nLineHeight );
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
                break;
            case UNDERLINE_DOTTED:
            case UNDERLINE_BOLDDOTTED:
                {
                    long nDotWidth = nLineHeight*mnDPIY;
                    nDotWidth += mnDPIY/2;
                    nDotWidth /= mnDPIY;
                    long nTempWidth = nDotWidth;
                    long nEnd = nLeft+nWidth;
                    while ( nLeft < nEnd )
                    {
                        if ( nLeft+nTempWidth > nEnd )
                            nTempWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
                        nLeft += nDotWidth*2;
                    }
                }
                break;
            case UNDERLINE_DASH:
            case UNDERLINE_LONGDASH:
            case UNDERLINE_BOLDDASH:
            case UNDERLINE_BOLDLONGDASH:
                {
                    long nDotWidth = nLineHeight*mnDPIY;
                    nDotWidth += mnDPIY/2;
                    nDotWidth /= mnDPIY;
                    long nMinDashWidth;
                    long nMinSpaceWidth;
                    long nSpaceWidth;
                    long nDashWidth;
                    if ( (eTextLine == UNDERLINE_LONGDASH) ||
                         (eTextLine == UNDERLINE_BOLDLONGDASH) )
                    {
                        nMinDashWidth = nDotWidth*6;
                        nMinSpaceWidth = nDotWidth*2;
                        nDashWidth = 200;
                        nSpaceWidth = 100;
                    }
                    else
                    {
                        nMinDashWidth = nDotWidth*4;
                        nMinSpaceWidth = (nDotWidth*150)/100;
                        nDashWidth = 100;
                        nSpaceWidth = 50;
                    }
                    nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
                    nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
                    // DashWidth wird gegebenenfalls verbreitert, wenn
                    // die dicke der Linie im Verhaeltnis zur Laenge
                    // zu dick wird
                    if ( nDashWidth < nMinDashWidth )
                        nDashWidth = nMinDashWidth;
                    if ( nSpaceWidth < nMinSpaceWidth )
                        nSpaceWidth = nMinSpaceWidth;
                    long nTempWidth = nDashWidth;
                    long nEnd = nLeft+nWidth;
                    while ( nLeft < nEnd )
                    {
                        if ( nLeft+nTempWidth > nEnd )
                            nTempWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
                        nLeft += nDashWidth+nSpaceWidth;
                    }
                }
                break;
            case UNDERLINE_DASHDOT:
            case UNDERLINE_BOLDDASHDOT:
                {
                    long nDotWidth = nLineHeight*mnDPIY;
                    nDotWidth += mnDPIY/2;
                    nDotWidth /= mnDPIY;
                    long nDashWidth = ((100*mnDPIX)+1270)/2540;
                    long nMinDashWidth = nDotWidth*4;
                    // DashWidth wird gegebenenfalls verbreitert, wenn
                    // die dicke der Linie im Verhaeltnis zur Laenge
                    // zu dick wird
                    if ( nDashWidth < nMinDashWidth )
                        nDashWidth = nMinDashWidth;
                    long nTempDotWidth = nDotWidth;
                    long nTempDashWidth = nDashWidth;
                    long nEnd = nLeft+nWidth;
                    while ( nLeft < nEnd )
                    {
                        if ( nLeft+nTempDotWidth > nEnd )
                            nTempDotWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
                        nLeft += nDotWidth*2;
                        if ( nLeft > nEnd )
                            break;
                        if ( nLeft+nTempDashWidth > nEnd )
                            nTempDashWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
                        nLeft += nDashWidth+nDotWidth;
                    }
                }
                break;
            case UNDERLINE_DASHDOTDOT:
            case UNDERLINE_BOLDDASHDOTDOT:
                {
                    long nDotWidth = nLineHeight*mnDPIY;
                    nDotWidth += mnDPIY/2;
                    nDotWidth /= mnDPIY;
                    long nDashWidth = ((100*mnDPIX)+1270)/2540;
                    long nMinDashWidth = nDotWidth*4;
                    // DashWidth wird gegebenenfalls verbreitert, wenn
                    // die dicke der Linie im Verhaeltnis zur Laenge
                    // zu dick wird
                    if ( nDashWidth < nMinDashWidth )
                        nDashWidth = nMinDashWidth;
                    long nTempDotWidth = nDotWidth;
                    long nTempDashWidth = nDashWidth;
                    long nEnd = nLeft+nWidth;
                    while ( nLeft < nEnd )
                    {
                        if ( nLeft+nTempDotWidth > nEnd )
                            nTempDotWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
                        nLeft += nDotWidth*2;
                        if ( nLeft > nEnd )
                            break;
                        if ( nLeft+nTempDotWidth > nEnd )
                            nTempDotWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
                        nLeft += nDotWidth*2;
                        if ( nLeft > nEnd )
                            break;
                        if ( nLeft+nTempDashWidth > nEnd )
                            nTempDashWidth = nEnd-nLeft;
                        ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
                        nLeft += nDashWidth+nDotWidth;
                    }
                }
                break;
            default:
                break;
        }
    }
}

// -----------------------------------------------------------------------

void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY,
                                          long nX, long nY, long nWidth,
                                          FontStrikeout eStrikeout,
                                          Color aColor )
{
    BOOL bOldMap = IsMapModeEnabled();
    EnableMapMode( FALSE );

    Color aOldColor = GetTextColor();
    SetTextColor( aColor );
    ImplInitTextColor();

    xub_Unicode pChars[5];
    if ( eStrikeout == STRIKEOUT_SLASH )
        pChars[0] = '/';
    else // ( eStrikeout == STRIKEOUT_X )
        pChars[0] = 'X';
    pChars[3]=pChars[2]=pChars[1]=pChars[0];

    // calculate approximation of strikeout atom size
    long nStrikeoutWidth = nWidth;
    String aStrikeoutTest( pChars, 4 );
    SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, 4 );
    if ( pLayout )
    {
        nStrikeoutWidth = (pLayout->GetTextWidth() + 2) / 4;
        pLayout->Release();
        if ( nStrikeoutWidth <= 0 ) // sanity check
            nStrikeoutWidth = 1;
    }

    // calculate acceptable strikeout length
    // allow the strikeout to be one pixel larger than the text it strikes out
    long nMaxWidth = nStrikeoutWidth/2;
    if ( nMaxWidth < 2 )
        nMaxWidth = 2;
    nMaxWidth += nWidth + 1;

    // build strikeout string
    long nFullStrikeoutWidth = 0;
    String aStrikeoutText( pChars, 0 );
    while ( (nFullStrikeoutWidth+=nStrikeoutWidth) < nMaxWidth+1 )
        aStrikeoutText += pChars[0];

    // if the text width is smaller than the strikeout text, then do not
    // strike out at all. This case requires user interaction, e.g. adding
    // a space to the text
    if ( (aStrikeoutText.Len() > 0) )
#ifdef AVS
    && !(mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
#endif
    {
        if ( mpFontEntry->mnOrientation )
            ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );

        // strikeout text has to be left aligned
        ULONG nOrigTLM = mnTextLayoutMode;
        mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
        SalLayout* pSalLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN );
        mnTextLayoutMode = nOrigTLM;

        if ( pSalLayout )
        {
            pSalLayout->DrawBase() = Point( nX+mnTextOffX, nY+mnTextOffY );
            pSalLayout->DrawText( *mpGraphics );
            pSalLayout->Release();
        }
    }

    SetTextColor( aOldColor );
    ImplInitTextColor();
    EnableMapMode( bOldMap );
}

// -----------------------------------------------------------------------

void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY,
                                          long nX, long nY, long nWidth,
                                          FontStrikeout eStrikeout,
                                          Color aColor )
{
    ImplFontEntry*  pFontEntry = mpFontEntry;
    long            nLineHeight = 0;
    long            nLinePos  = 0;
    long            nLinePos2 = 0;

    if ( eStrikeout > STRIKEOUT_LAST )
        eStrikeout = STRIKEOUT_SINGLE;

    switch ( eStrikeout )
    {
        case STRIKEOUT_SINGLE:
            nLineHeight = pFontEntry->maMetric.mnStrikeoutSize;
            nLinePos    = nY + pFontEntry->maMetric.mnStrikeoutOffset;
            break;
        case STRIKEOUT_BOLD:
            nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
            nLinePos    = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
            break;
        case STRIKEOUT_DOUBLE:
            nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
            nLinePos    = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
            nLinePos2   = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
            break;
        default:
            break;
    }

    if ( nLineHeight )
    {
        if ( mbLineColor || mbInitLineColor )
        {
            mpGraphics->SetLineColor();
            mbInitLineColor = TRUE;
        }
        mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
        mbInitFillColor = TRUE;

        long nLeft = nX;

        switch ( eStrikeout )
        {
            case STRIKEOUT_SINGLE:
            case STRIKEOUT_BOLD:
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
                break;
            case STRIKEOUT_DOUBLE:
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
                ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
                break;
            default:
                break;
        }
    }
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY,
                                         long nX, long nY, long nWidth,
                                         FontUnderline eTextLine,
                                         Color aColor,
                                         BOOL bIsAbove )
{
    ImplFontEntry*  pFontEntry = mpFontEntry;
    long            nLineHeight;
    long            nLinePos;

    if ( bIsAbove )
    {
        nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
        nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
    }
    else
    {
        nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
        nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
    }
    if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
        nLineHeight = 3;
    long nLineWidth = (mnDPIX/300);
    if ( !nLineWidth )
        nLineWidth = 1;
    if ( eTextLine == UNDERLINE_BOLDWAVE )
        nLineWidth *= 2;
    nLinePos += nY - (nLineHeight / 2);
    long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
    if ( eTextLine == UNDERLINE_DOUBLEWAVE )
    {
        long nOrgLineHeight = nLineHeight;
        nLineHeight /= 3;
        if ( nLineHeight < 2 )
        {
            if ( nOrgLineHeight > 1 )
                nLineHeight = 2;
            else
                nLineHeight = 1;
        }
        long nLineDY = nOrgLineHeight-(nLineHeight*2);
        if ( nLineDY < nLineWidthHeight )
            nLineDY = nLineWidthHeight;
        long nLineDY2 = nLineDY/2;
        if ( !nLineDY2 )
            nLineDY2 = 1;

        nLinePos -= nLineWidthHeight-nLineDY2;
        ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
                          nLineWidth, mpFontEntry->mnOrientation, aColor );
        nLinePos += nLineWidthHeight+nLineDY;
        ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
                          nLineWidth, mpFontEntry->mnOrientation, aColor );
    }
    else
    {
        nLinePos -= nLineWidthHeight/2;
        ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
                          nLineWidth, mpFontEntry->mnOrientation, aColor );
    }
}
// -----------------------------------------------------------------------

static void ImplDrawWavePixel( long nOriginX, long nOriginY,
                               long nCurX, long nCurY,
                               short nOrientation,
                               SalGraphics* pGraphics,
                               OutputDevice* pOutDev,
                               BOOL bDrawPixAsRect,

                               long nPixWidth, long nPixHeight )
{
    if ( nOrientation )
        ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation );

    if ( bDrawPixAsRect )
    {

        pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
    }
    else
    {
        pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
    }
}
// -----------------------------------------------------------------------

void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY,
                                     long nStartX, long nStartY,
                                     long nWidth, long nHeight,
                                     long nLineWidth, short nOrientation,
                                     const Color& rColor )
{
    if ( !nHeight )
        return;

    // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben
    if ( (nLineWidth == 1) && (nHeight == 1) )
    {
        mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
        mbInitLineColor = TRUE;

        long nEndX = nStartX+nWidth;
        long nEndY = nStartY;
        if ( nOrientation )
        {
            ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
            ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation );
        }
        mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );

    }
    else
    {
        long    nCurX = nStartX;
        long    nCurY = nStartY;
        long    nDiffX = 2;
        long    nDiffY = nHeight-1;
        long    nCount = nWidth;
        long    nOffY = -1;
        long    nFreq;
        long    i;
        long    nPixWidth;
        long    nPixHeight;
        BOOL    bDrawPixAsRect;
        // Auf Druckern die Pixel per DrawRect() ausgeben
        if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
        {
            if ( mbLineColor || mbInitLineColor )
            {
                mpGraphics->SetLineColor();
                mbInitLineColor = TRUE;
            }
            mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
            mbInitFillColor = TRUE;
            bDrawPixAsRect  = TRUE;
            nPixWidth       = nLineWidth;
            nPixHeight      = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
        }
        else
        {
            mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
            mbInitLineColor = TRUE;
            nPixWidth       = 1;
            nPixHeight      = 1;
            bDrawPixAsRect  = FALSE;
        }

        if ( !nDiffY )
        {
            while ( nWidth )
            {
                ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
                                   mpGraphics, this,
                                   bDrawPixAsRect, nPixWidth, nPixHeight );
                nCurX++;
                nWidth--;
            }
        }
        else
        {
            nCurY += nDiffY;
            nFreq = nCount / (nDiffX+nDiffY);
            while ( nFreq-- )
            {
                for( i = nDiffY; i; --i )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
                                       mpGraphics, this,
                                       bDrawPixAsRect, nPixWidth, nPixHeight );
                    nCurX++;
                    nCurY += nOffY;
                }
                for( i = nDiffX; i; --i )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
                                       mpGraphics, this,
                                       bDrawPixAsRect, nPixWidth, nPixHeight );
                    nCurX++;
                }
                nOffY = -nOffY;
            }
            nFreq = nCount % (nDiffX+nDiffY);
            if ( nFreq )
            {
                for( i = nDiffY; i && nFreq; --i, --nFreq )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
                                       mpGraphics, this,
                                       bDrawPixAsRect, nPixWidth, nPixHeight );
                    nCurX++;
                    nCurY += nOffY;

                }
                for( i = nDiffX; i && nFreq; --i, --nFreq )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
                                       mpGraphics, this,
                                       bDrawPixAsRect, nPixWidth, nPixHeight );
                    nCurX++;
                }
            }
        }

    }
}

// -----------------------------------------------------------------------

BOOL OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
{
    if ( !rFont.IsVertical() )
        return FALSE;

    if( (LANGUAGE_JAPANESE == rFont.GetLanguage())
    ||  (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
        // the underline is right for Japanese only
        return TRUE;

    return FALSE;
}
// -----------------------------------------------------------------------

bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
{
#ifdef QWE
    int nX = rSalLayout.DrawBase().X();
    int nY = rSalLayout.DrawBase().Y();

    Rectangle aBoundRect;
    rSalLayout.DrawBase() = Point( 0, 0 );
    rSalLayout.DrawOffset() = Point( 0, 0 );
    if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
    {
        // guess vertical text extents if GetBoundRect failed
        int nRight = rSalLayout.GetTextWidth();
        int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
        long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
        aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
    }
#ifdef AVS
    // cache virtual device for rotation
    if ( !mpOutDevData )
        ImplInitOutDevData();
    if ( !mpOutDevData->mpRotateDev )
        mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 );
    VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
#endif
    VirtualDevice* pVDev = new VirtualDevice( *this, 1 );
    // size it accordingly
    if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
        return false;

    Font aFont( GetFont() );
    aFont.SetOrientation( 0 );
    aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
    pVDev->SetFont( aFont );
    pVDev->SetTextColor( Color( COL_BLACK ) );
    pVDev->SetTextFillColor();
    pVDev->ImplNewFont();
    pVDev->ImplInitFont();
    pVDev->ImplInitTextColor();

    // draw text into upper left corner
    rSalLayout.DrawBase() -= aBoundRect.TopLeft();
    rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics );

    Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
    if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
        return false;

    // calculate rotation offset
    Polygon aPoly( aBoundRect );
    aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
    Point aPoint = aPoly.GetBoundRect().TopLeft();
    aPoint += Point( nX, nY );
#ifdef AVS
    // mask output with text colored bitmap
    GDIMetaFile* pOldMetaFile = mpMetaFile;
#endif
    long nOldOffX = mnOutOffX;
    long nOldOffY = mnOutOffY;
    BOOL bOldMap = mbMap;

    mnOutOffX   = 0L;
    mnOutOffY   = 0L;
#ifdef AVS
    mpMetaFile  = NULL;
#endif
    EnableMapMode( FALSE );

    DrawMask( aPoint, aBmp, GetTextColor() );

    EnableMapMode( bOldMap );
    mnOutOffX   = nOldOffX;
    mnOutOffY   = nOldOffY;
#ifdef AVS
    mpMetaFile  = pOldMetaFile;
#endif
#endif
    return true;
}

// -----------------------------------------------------------------------

void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
{
#ifdef QWE
    Color       aOldColor           = GetTextColor();
    Color       aOldTextLineColor   = GetTextLineColor();
    Color       aOldOverlineColor   = GetOverlineColor();
    FontRelief  eRelief             = maFont.GetRelief();

    Point aOrigPos = rSalLayout.DrawBase();
    if ( eRelief != RELIEF_NONE )
    {
        Color   aReliefColor( COL_LIGHTGRAY );
        Color   aTextColor( aOldColor );

        Color   aTextLineColor( aOldTextLineColor );
        Color   aOverlineColor( aOldOverlineColor );

        // we don't have a automatic color, so black is always drawn on white
        if ( aTextColor.GetColor() == COL_BLACK )
            aTextColor = Color( COL_WHITE );
        if ( aTextLineColor.GetColor() == COL_BLACK )
            aTextLineColor = Color( COL_WHITE );
        if ( aOverlineColor.GetColor() == COL_BLACK )
            aOverlineColor = Color( COL_WHITE );

        // relief-color is black for white text, in all other cases
        // we set this to LightGray
        if ( aTextColor.GetColor() == COL_WHITE )
            aReliefColor = Color( COL_BLACK );
        SetTextLineColor( aReliefColor );
        SetOverlineColor( aReliefColor );
        SetTextColor( aReliefColor );
        ImplInitTextColor();

        // calculate offset - for high resolution printers the offset
        // should be greater so that the effect is visible
        long nOff = 1;
        nOff += mnDPIX/300;

        if ( eRelief == RELIEF_ENGRAVED )
            nOff = -nOff;
        rSalLayout.DrawOffset() += Point( nOff, nOff);
        ImplDrawTextDirect( rSalLayout, mbTextLines );
        rSalLayout.DrawOffset() -= Point( nOff, nOff);

        SetTextLineColor( aTextLineColor );
        SetOverlineColor( aOverlineColor );
        SetTextColor( aTextColor );
        ImplInitTextColor();
        ImplDrawTextDirect( rSalLayout, mbTextLines );

        SetTextLineColor( aOldTextLineColor );
        SetOverlineColor( aOldOverlineColor );

        if ( aTextColor != aOldColor )
        {
            SetTextColor( aOldColor );
            ImplInitTextColor();
        }
    }
    else
    {
        if ( maFont.IsShadow() )
        {
            long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
            if ( maFont.IsOutline() )
                nOff++;
            SetTextLineColor();
            SetOverlineColor();
            if ( (GetTextColor().GetColor() == COL_BLACK)
            ||   (GetTextColor().GetLuminance() < 8) )
                SetTextColor( Color( COL_LIGHTGRAY ) );
            else
                SetTextColor( Color( COL_BLACK ) );
            ImplInitTextColor();
            rSalLayout.DrawBase() += Point( nOff, nOff );
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() -= Point( nOff, nOff );
            SetTextColor( aOldColor );
            SetTextLineColor( aOldTextLineColor );
            SetOverlineColor( aOldOverlineColor );
            ImplInitTextColor();

            if ( !maFont.IsOutline() )
                ImplDrawTextDirect( rSalLayout, mbTextLines );
        }

        if ( maFont.IsOutline() )
        {
            rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            rSalLayout.DrawBase() = aOrigPos;

            SetTextColor( Color( COL_WHITE ) );
            SetTextLineColor( Color( COL_WHITE ) );
            SetOverlineColor( Color( COL_WHITE ) );
            ImplInitTextColor();
            ImplDrawTextDirect( rSalLayout, mbTextLines );
            SetTextColor( aOldColor );
            SetTextLineColor( aOldTextLineColor );
            SetOverlineColor( aOldOverlineColor );
            ImplInitTextColor();
        }
    }
#endif
}
// -----------------------------------------------------------------------

void OutputDevice::ImplInitTextColor()
{
    if ( mbInitTextColor )
    {
        mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
        mbInitTextColor = FALSE;
    }
}

SalLayout* OutputDevice::ImplLayout( const String& rOrigStr,
                                     xub_StrLen nMinIndex,
                                     xub_StrLen nLen,
                                     const Point& rLogicalPos,
                                     long nLogicalWidth,
                                     const sal_Int32* pDXArray,
                                     bool bFilter )  const
{

    // we need a graphics
    if( !mpGraphics )
            return NULL;

    // initialize font if needed
    if( mbNewFont )
        if( !ImplNewFont() )
            return NULL;
    if( mbInitFont )
        ImplInitFont();

    // check string index and length
    String aStr = rOrigStr;
    if( (ULONG)nMinIndex + nLen >= aStr.Len() )
        if( nMinIndex < aStr.Len() )
            nLen = aStr.Len() - nMinIndex;
        else
            return NULL;

    // filter out special markers
    if( bFilter )
    {
        xub_StrLen nCutStart, nCutStop, nOrgLen = nLen;
        bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop );
        if( !nLen )
            return NULL;

        if( bFiltered && nCutStop != nCutStart && pDXArray )
        {
            if( !nLen )
                pDXArray = NULL;
            else
            {
                sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
                if( nCutStart > nMinIndex )
                    memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) );
                // note: nCutStart will never be smaller than nMinIndex
                memcpy( pAry+nCutStart-nMinIndex,
                        pDXArray + nOrgLen - (nCutStop-nMinIndex),
                        sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) );
                pDXArray = pAry;
            }
        }
    }

    // convert from logical units to physical units
    // recode string if needed
    if( mpFontEntry->mpConversion )
        ImplRecodeString( mpFontEntry->mpConversion, aStr, 0, aStr.Len() );

    long nPixelWidth = nLogicalWidth;
    if( nLogicalWidth && mbMap )
        nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
    if( pDXArray && mbMap )
    {
        // convert from logical units to font units using a temporary array
        sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
        // using base position for better rounding a.k.a. "dancing characters"
        int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
        for( int i = 0; i < nLen; ++i )
            pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;

        pDXArray = pTempDXAry;
    }

    ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );

    // get matching layout object for base font
    SalLayout* pSalLayout = NULL;
#ifdef AVS
    if( mpPDFWriter )
        pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData );
#endif
    if( !pSalLayout )
        pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );

    // layout text
    if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
    {
        pSalLayout->Release();
        pSalLayout = NULL;
    }

    if( !pSalLayout )
        return NULL;
#ifdef QWE
    // do glyph fallback if needed
    // #105768# avoid fallback for very small font sizes
    if( aLayoutArgs.NeedFallback() )
        if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) )
            pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs );
#endif
    // position, justify, etc. the layout
    pSalLayout->AdjustLayout( aLayoutArgs );
    pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
    // adjust to right alignment if necessary
    if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
    {
        long nRTLOffset;
        if( pDXArray )
            nRTLOffset = pDXArray[ nLen - 1 ];
        else if( nPixelWidth )
            nRTLOffset = nPixelWidth;
        else
            nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
        pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
    }

    return pSalLayout;
}
#ifdef QWE
// -----------------------------------------------------------------------

SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
{
    // prepare multi level glyph fallback
    MultiSalLayout* pMultiSalLayout = NULL;
    ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
    rLayoutArgs.PrepareFallback();
    rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;

#if defined(HDU_DEBUG)
    {
        int nCharPos = -1;
        bool bRTL = false;
        fprintf(stderr,"OD:ImplLayout Glyph Fallback for");
        for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i )
            fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] );
        fprintf(stderr,"\n");
        rLayoutArgs.ResetPos();
    }
#endif
    // get list of unicodes that need glyph fallback
    int nCharPos = -1;
    bool bRTL = false;
    rtl::OUStringBuffer aMissingCodeBuf;
    while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
        aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
    rLayoutArgs.ResetPos();
    rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();

    ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData;
    // when device specific font substitution may have been performed for
    // the originally selected font then make sure that a fallback to that
    // font is performed first
    int nDevSpecificFallback = 0;
    if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() )
        nDevSpecificFallback = 1;

    // try if fallback fonts support the missing unicodes
    for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
    {
        // find a font family suited for glyph fallback
#ifndef FONTFALLBACK_HOOKS_DISABLED 
        // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
        // if the system-specific glyph fallback is active
        aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
#endif
        ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList,
            aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes );
        if( !pFallbackFont )
            break;

        aFontSelData.mpFontEntry = pFallbackFont;
        aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
        if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
        {
            // ignore fallback font if it is the same as the original font
            if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
            {
                mpFontCache->Release( pFallbackFont );
                continue;
            }
        }

#if defined(HDU_DEBUG)
        {
            ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8);
            ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(),
                RTL_TEXTENCODING_UTF8);
            fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n",
                nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(),
                aFontSelData.mpFontData->GetQuality());
        }
#endif

        pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );

        // create and add glyph fallback layout to multilayout
        rLayoutArgs.ResetPos();
        SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
        if( pFallback )
        {
            if( pFallback->LayoutText( rLayoutArgs ) )
            {
                if( !pMultiSalLayout )
                    pMultiSalLayout = new MultiSalLayout( *pSalLayout );
                pMultiSalLayout->AddFallback( *pFallback,
                    rLayoutArgs.maRuns, aFontSelData.mpFontData );
                if (nFallbackLevel == MAX_FALLBACK-1)
                    pMultiSalLayout->SetInComplete();
            }
            else
            {
                // there is no need for a font that couldn't resolve anything
                pFallback->Release();
            }
        }

        mpFontCache->Release( pFallbackFont );

        // break when this fallback was sufficient
        if( !rLayoutArgs.PrepareFallback() )
            break;
    }

    if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
        pSalLayout = pMultiSalLayout;

    // restore orig font settings
    pSalLayout->InitFont();
    rLayoutArgs.maRuns = aLayoutRuns;

    return pSalLayout;
}
#endif
// -----------------------------------------------------------------------

ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr,
                                       xub_StrLen nMinIndex, xub_StrLen nLen,
                                       long nPixelWidth, const sal_Int32* pDXArray ) const
{
    // get string length for calculating extents
    xub_StrLen nEndIndex = rStr.Len();
    if( (ULONG)nMinIndex + nLen < nEndIndex )
        nEndIndex = nMinIndex + nLen;

    // don't bother if there is nothing to do
    if( nEndIndex < nMinIndex )
        nEndIndex = nMinIndex;

    int nLayoutFlags = 0;
    if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
        nLayoutFlags |= SAL_LAYOUT_BIDI_RTL;
    if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
        nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
    else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
    {
        // disable Bidi if no RTL hint and no RTL codes used
        const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
        const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
        for( ; pStr < pEnd; ++pStr )
            if( ((*pStr >= 0x0580) && (*pStr < 0x0800))   // middle eastern scripts
            ||  ((*pStr >= 0xFB18) && (*pStr < 0xFE00))   // hebrew + arabic A presentation forms
            ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
                break;
        if( pStr >= pEnd )
            nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
    }

    if( mbKerning )
        nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS;
    if( maFont.GetKerning() & KERNING_ASIAN )
        nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN;
    if( maFont.IsVertical() )
        nLayoutFlags |= SAL_LAYOUT_VERTICAL;

    if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
        nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES;
    else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
        nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
    else
    {
        // disable CTL for non-CTL text
        const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
        const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
        for( ; pStr < pEnd; ++pStr )
            if( ((*pStr >= 0x0300) && (*pStr < 0x0370))   // diacritical marks
            ||  ((*pStr >= 0x0590) && (*pStr < 0x10A0))   // many CTL scripts
            ||  ((*pStr >= 0x1100) && (*pStr < 0x1200))   // hangul jamo
            ||  ((*pStr >= 0x1700) && (*pStr < 0x1900))   // many CTL scripts
            ||  ((*pStr >= 0xFB1D) && (*pStr < 0xFE00))   // middle east presentation
            ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation B
                break;
        if( pStr >= pEnd )
            nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
    }

    if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
    {
        // disable character localization when no digits used
        const sal_Unicode* pBase = rStr.GetBuffer();
        const sal_Unicode* pStr = pBase + nMinIndex;
        const sal_Unicode* pEnd = pBase + nEndIndex;
        for( ; pStr < pEnd; ++pStr )
        {
            // TODO: are there non-digit localizations?
            if( (*pStr >= '0') && (*pStr <= '9') )
            {
                // translate characters to local preference
                sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
                if( cChar != *pStr )
                    // TODO: are the localized digit surrogates?
                    rStr.SetChar( static_cast<USHORT>(pStr - pBase),
                                 static_cast<sal_Unicode>(cChar) );
            }
        }
    }

    // right align for RTL text, DRAWPOS_REVERSED, RTL window style
    bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
    if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
        bRightAlign = false;
    else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
        bRightAlign = true;
    // SSA: hack for western office, ie text get right aligned
    //      for debugging purposes of mirrored UI
    //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" );
    bool bRTLWindow = IsRTLEnabled();
    bRightAlign ^= bRTLWindow;
    if( bRightAlign )
        nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;

    // set layout options
    ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags );

    int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
    aLayoutArgs.SetOrientation( nOrientation );

    aLayoutArgs.SetLayoutWidth( nPixelWidth );
    aLayoutArgs.SetDXArray( pDXArray );

    return aLayoutArgs;
}


// =======================================================================

void OutputDevice::ImplInitFont() const
{
    //DBG_TESTSOLARMUTEX();

    if ( mbInitFont )
    {
        if ( meOutDevType != OUTDEV_PRINTER )
        {
            // decide if antialiasing is appropriate
            bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0;
#ifdef AVS
            const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
            bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0);
            bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight);
#endif
            mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased;
        }
#ifdef AVS
        if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
        {
#endif
            // select font in the device layers
            mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 );
#ifdef AVS
        }
#endif
        mbInitFont = false;
    }
}

// -----------------------------------------------------------------------

bool OutputDevice::ImplNewFont() const
{
    //DBG_TESTSOLARMUTEX();
#ifdef AVS
    // get correct font list on the PDF writer if necessary
    if( mpPDFWriter )
    {
        const ImplSVData* pSVData = ImplGetSVData();
        if( mpFontList == pSVData->maGDIData.mpScreenFontList
        ||  mpFontCache == pSVData->maGDIData.mpScreenFontCache )
            const_cast<OutputDevice&>(*this).ImplUpdateFontData( true );
    }
#endif
    if ( !mbNewFont )
        return true;

    // we need a graphics
    if ( !mpGraphics )
        return false;
    SalGraphics* pGraphics = mpGraphics;
    ImplInitFontList();

    // convert to pixel height
    // TODO: replace integer based aSize completely with subpixel accurate type
    float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) );
    Size aSize = ImplLogicToDevicePixel( maFont.GetSize() );
    if ( !aSize.Height() )
    {
        // use default pixel height only when logical height is zero
        if ( maFont.GetSize().Height() )
            aSize.Height() = 1;
        else
            aSize.Height() = (12*mnDPIY)/72;
        fExactHeight =  static_cast<float>(aSize.Height());
    }

    // select the default width only when logical width is zero
    if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) )
        aSize.Width() = 1;

    // get font entry
    ImplDirectFontSubstitution* pDevSpecificSubst = NULL;
#ifdef AVS
    if( mpOutDevData )
        pDevSpecificSubst = &mpOutDevData->maDevFontSubst;
#endif
    ImplFontEntry* pOldEntry = mpFontEntry;
    mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst );

    if( pOldEntry )
        mpFontCache->Release( pOldEntry );

    ImplFontEntry* pFontEntry = mpFontEntry;
    // mark when lower layers need to get involved
    mbNewFont = FALSE;
    if( pFontEntry != pOldEntry )
        mbInitFont = TRUE;

    // select font when it has not been initialized yet
    if ( !pFontEntry->mbInit )
    {
        ImplInitFont();

        // get metric data from device layers
        if ( pGraphics )
        {
            pFontEntry->mbInit = true;

            pFontEntry->maMetric.mnOrientation  = static_cast<short>(pFontEntry->maFontSelData.mnOrientation);
#ifdef AVS
            if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) )
                mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) );
            else
#endif
                pGraphics->GetFontMetric( &(pFontEntry->maMetric) );

            pFontEntry->maMetric.ImplInitTextLineSize( this );
            pFontEntry->maMetric.ImplInitAboveTextLineSize();

            pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent;

            if( pFontEntry->maFontSelData.mnOrientation
            && !pFontEntry->maMetric.mnOrientation
            && (meOutDevType != OUTDEV_PRINTER) )
            {
                pFontEntry->mnOwnOrientation = static_cast<short>(pFontEntry->maFontSelData.mnOrientation);
                pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation;
            }
            else
                pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation;
        }
    }

    // enable kerning array if requested
    if ( maFont.GetKerning() & KERNING_FONTSPECIFIC )
    {
        // TODO: test if physical font supports kerning and disable if not
        if( pFontEntry->maMetric.mbKernableFont )
            mbKerning = true;
    }
    else
        mbKerning = false;
    if ( maFont.GetKerning() & KERNING_ASIAN )
        mbKerning = true;

    // calculate EmphasisArea
    mnEmphasisAscent = 0;
    mnEmphasisDescent = 0;
    if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
    {
        FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
        long                nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000;
        if ( nEmphasisHeight < 1 )
            nEmphasisHeight = 1;
        if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
            mnEmphasisDescent = nEmphasisHeight;
        else
            mnEmphasisAscent = nEmphasisHeight;
    }

    // calculate text offset depending on TextAlignment
    TextAlign eAlign = maFont.GetAlign();
    if ( eAlign == ALIGN_BASELINE )
    {
        mnTextOffX = 0;
        mnTextOffY = 0;
    }
    else if ( eAlign == ALIGN_TOP )
    {
        mnTextOffX = 0;
        mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent;
        if ( pFontEntry->mnOrientation )
            ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
    }
    else // eAlign == ALIGN_BOTTOM
    {
        mnTextOffX = 0;
        mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent;
        if ( pFontEntry->mnOrientation )
            ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
    }

    mbTextLines     = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) ||
                      ((maFont.GetOverline()  != UNDERLINE_NONE) && (maFont.GetOverline()  != UNDERLINE_DONTKNOW)) ||
                      ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
    mbTextSpecial   = maFont.IsShadow() || maFont.IsOutline() ||
                      (maFont.GetRelief() != RELIEF_NONE);

    // #95414# fix for OLE objects which use scale factors very creatively
    if( mbMap && !aSize.Width() )
    {
        int nOrigWidth = pFontEntry->maMetric.mnWidth;
        float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY;
        fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX;
        int nNewWidth = (int)(nOrigWidth * fStretch + 0.5);
        if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
        {
            Size aOrigSize = maFont.GetSize();
            const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) );
            mbMap = FALSE;
            mbNewFont = TRUE;
            ImplNewFont();  // recurse once using stretched width
            mbMap = TRUE;
            const_cast<Font&>(maFont).SetSize( aOrigSize );
        }
    }
    return true;
}
// =======================================================================

FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont ) const
{
    FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();

    // If no Position is set, then calculate the default position, which
    // depends on the language
    if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) )
    {
        LanguageType eLang = rFont.GetLanguage();
        // In Chinese Simplified the EmphasisMarks are below/left
        if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
             (eLang == LANGUAGE_CHINESE_SINGAPORE) )
            nEmphasisMark |= EMPHASISMARK_POS_BELOW;
        else
        {
            eLang = rFont.GetCJKContextLanguage();
            // In Chinese Simplified the EmphasisMarks are below/left
            if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
                 (eLang == LANGUAGE_CHINESE_SINGAPORE) )
                nEmphasisMark |= EMPHASISMARK_POS_BELOW;
            else
                nEmphasisMark |= EMPHASISMARK_POS_ABOVE;
        }
    }

    return nEmphasisMark;
}
// =======================================================================

void OutputDevice::ImplInitFontList() const
{
    if( ! mpFontList->Count() )
    {
        if( mpGraphics )
        {
			//RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" );
            mpGraphics->GetDevFontList( mpFontList );
        }
    }
}
void OutputDevice::DrawTextArray( const Point& rStartPt, const XubString& rStr,
                                       const sal_Int32* pDXAry,
                                       xub_StrLen nIndex,
                                       xub_StrLen nLen )
{
    SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
    if( pSalLayout )
    {
        ImplDrawText( *pSalLayout );
        pSalLayout->Release();
    }
}
void OutputDevice::DrawStretchText( const Point& rStartPt, ULONG nWidth,
                                         const XubString& rStr,
                                         xub_StrLen nIndex , xub_StrLen nLen )
{
    SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true );
    if( pSalLayout )
    {
        ImplDrawText( *pSalLayout );
        pSalLayout->Release();
    }
}
void OutputDevice::DrawText( const Rectangle& rRect,
                                  const XubString& rOrigStr, USHORT nStyle,
                                  MetricVector* pVector , String* pDisplayText )
{
    if ( ! pVector || !rOrigStr.Len() || rRect.IsEmpty() )
        return;

    // we need a graphics
    if( !mpGraphics )
        return;
    if( mbInitClipRegion )
        ImplInitClipRegion();
    if( mbOutputClipped )
        return;

    // #i47157# Factored out to ImplDrawTextRect(), to be used also
    // from AddTextRectActions()
    ImplDrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
}
void OutputDevice::DrawTextLine( const Point& rPos, long nWidth,
                                      FontStrikeout eStrikeout,
                                      FontUnderline eUnderline,
                                      FontUnderline eOverline,
                                      BOOL bUnderlineAbove )
{
    if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
         ((eOverline  == UNDERLINE_NONE) || (eOverline  == UNDERLINE_DONTKNOW)) &&
         ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
        return;

    // we need a graphics
    if( !mpGraphics )
        return;
    if( mbInitClipRegion )
        ImplInitClipRegion();
    if( mbOutputClipped )
        return;

    // initialize font if needed to get text offsets
    // TODO: only needed for mnTextOff!=(0,0)
    if( mbNewFont )
        if( !ImplNewFont() )
            return;
    if( mbInitFont )
        ImplInitFont();

    Point aPos = ImplLogicToDevicePixel( rPos );
    nWidth = ImplLogicWidthToDevicePixel( nWidth );
    aPos += Point( mnTextOffX, mnTextOffY );
    ImplDrawTextLine( aPos.X(), aPos.X(), aPos.Y(), nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
}

void OutputDevice::DrawBitmap( const Point& rDestPt,
                                    const Bitmap& rBitmap )
{
	const Size aSizePix( rBitmap.GetSizePixel() );
	ImplDrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, META_BMP_ACTION );
}
void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
                                    const Bitmap& rBitmap )
{
	ImplDrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, META_BMPSCALE_ACTION );
}
void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
                                    const Point& rSrcPtPixel, const Size& rSrcSizePixel,
                                    const Bitmap& rBitmap )
{
	ImplDrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, META_BMPSCALEPART_ACTION );
}

void OutputDevice::ImplDrawBitmap( const Point& rDestPt, const Size& rDestSize,
								   const Point& rSrcPtPixel, const Size& rSrcSizePixel,
								   const Bitmap& rBitmap, const ULONG nAction )
{
	Bitmap aBmp( rBitmap );

	if ( ROP_INVERT == meRasterOp )
	{
		DrawRect( Rectangle( rDestPt, rDestSize ) );
		return;
	}
	OUTDEV_INIT();

	if( !aBmp.IsEmpty() )
	{
		SalTwoRect aPosAry;

		aPosAry.mnSrcX = rSrcPtPixel.X();
		aPosAry.mnSrcY = rSrcPtPixel.Y();
		aPosAry.mnSrcWidth = rSrcSizePixel.Width();
		aPosAry.mnSrcHeight = rSrcSizePixel.Height();
		aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
		aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
		aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
		aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );

		const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmp.GetSizePixel() );

		if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
		{
			if ( nMirrFlags )
				aBmp.Mirror( nMirrFlags );
            
            /* #i75264# (corrected with #i81576#)
            * sometimes a bitmap is scaled to a ridiculous size and drawn
            * to a quite normal VDev, so only a very small part of
            * the scaled bitmap will be visible. However actually scaling
            * the bitmap will use so much memory that we end with a crash.
            * Workaround: since only a small part of the scaled bitmap will
            * be actually drawn anyway (because of clipping on the device
            * boundary), limit the destination and source rectangles so
            * that the destination rectangle will overlap the device but only
            * be reasonably (say factor 2) larger than the device itself.
            */
            if( aPosAry.mnDestWidth > 2048 || aPosAry.mnDestHeight > 2048 )
            {
                // #i81576# do the following trick only if there is overlap at all
                // else the formulae don't work
                // theoretically in this case we wouldn't need to draw the bitmap at all
                // however there are some esoteric case where that is needed
                if( aPosAry.mnDestX + aPosAry.mnDestWidth >= 0
                    && aPosAry.mnDestX < mnOutWidth
                    && aPosAry.mnDestY + aPosAry.mnDestHeight >= 0
                    && aPosAry.mnDestY < mnOutHeight )
                {
                    // reduce scaling to something reasonable taking into account the output size
                    if( aPosAry.mnDestWidth > 3*mnOutWidth && aPosAry.mnSrcWidth )
                    {
                        const double nScaleX = aPosAry.mnDestWidth/double(aPosAry.mnSrcWidth);

                        if( aPosAry.mnDestX + aPosAry.mnDestWidth > mnOutWidth )
                        {
                            aPosAry.mnDestWidth = Max(long(0),mnOutWidth-aPosAry.mnDestX);
                        }
                        if( aPosAry.mnDestX < 0 )
                        {
                            aPosAry.mnDestWidth += aPosAry.mnDestX;
                            aPosAry.mnSrcX -= static_cast<long>(aPosAry.mnDestX / nScaleX);
                            aPosAry.mnDestX = 0;
                        }

                        aPosAry.mnSrcWidth = static_cast<long>(aPosAry.mnDestWidth / nScaleX);
                    }

                    if( aPosAry.mnDestHeight > 3*mnOutHeight && aPosAry.mnSrcHeight != 0 )
                    {
                        const double nScaleY = aPosAry.mnDestHeight/double(aPosAry.mnSrcHeight);

                        if( aPosAry.mnDestY + aPosAry.mnDestHeight > mnOutHeight )
                        {
                            aPosAry.mnDestHeight = Max(long(0),mnOutHeight-aPosAry.mnDestY);
                        }
                        if( aPosAry.mnDestY < 0 )
                        {
                            aPosAry.mnDestHeight += aPosAry.mnDestY;
                            aPosAry.mnSrcY -= static_cast<long>(aPosAry.mnDestY / nScaleY);
                            aPosAry.mnDestY = 0;
                        }

                        aPosAry.mnSrcHeight = static_cast<long>(aPosAry.mnDestHeight / nScaleY);
                    }
                }
            }

            if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
                mpGraphics->DrawBitmap( &aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap(), this );
		}
	}
}
void OutputDevice::DrawBitmapEx( const Point& rDestPt,
                                      const BitmapEx& rBitmapEx )
{
	if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
		DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
	else
	{
		const Size aSizePix( rBitmapEx.GetSizePixel() );
		ImplDrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, META_BMPEX_ACTION );
	}
}
void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
                                      const BitmapEx& rBitmapEx )
{
	if ( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
		DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
	else
		ImplDrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, META_BMPEXSCALE_ACTION );

}
void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
                                      const Point& rSrcPtPixel, const Size& rSrcSizePixel,
                                      const BitmapEx& rBitmapEx )
{
	if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
		DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
	else
		ImplDrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, META_BMPEXSCALEPART_ACTION );

}
void OutputDevice::ImplDrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
										const Point& rSrcPtPixel, const Size& rSrcSizePixel,
										const BitmapEx& rBitmapEx, const ULONG nAction )
{
	BitmapEx aBmpEx( rBitmapEx );

	if ( ROP_INVERT == meRasterOp )
	{
		DrawRect( Rectangle( rDestPt, rDestSize ) );
		return;
	}

	OUTDEV_INIT();

	if( aBmpEx.IsAlpha() )
	{
		ImplDrawAlpha( aBmpEx.GetBitmap(), aBmpEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
		return;
	}

	if( !( !aBmpEx ) )
	{
		SalTwoRect aPosAry;

		aPosAry.mnSrcX = rSrcPtPixel.X();
		aPosAry.mnSrcY = rSrcPtPixel.Y();
		aPosAry.mnSrcWidth = rSrcSizePixel.Width();
		aPosAry.mnSrcHeight = rSrcSizePixel.Height();
		aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
		aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
		aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
		aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );

		const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmpEx.GetSizePixel() );

		if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
		{

			if( nMirrFlags )
				aBmpEx.Mirror( nMirrFlags );

			const ImpBitmap* pImpBmp = aBmpEx.ImplGetBitmapImpBitmap();
			const ImpBitmap* pMaskBmp = aBmpEx.ImplGetMaskImpBitmap();

			if ( pMaskBmp )
            {
                // #4919452# reduce operation area to bounds of
                // cliprect. since masked transparency involves
                // creation of a large vdev and copying the screen
                // content into that (slooow read from framebuffer),
                // that should considerably increase performance for
                // large bitmaps and small clippings.

                // Note that this optimisation is a workaround for a
                // Writer peculiarity, namely, to decompose background
                // graphics into myriads of disjunct, tiny
                // rectangles. That otherwise kills us here, since for
                // transparent output, SAL always prepares the whole
                // bitmap, if aPosAry contains the whole bitmap (and
                // it's _not_ to blame for that).

                // Note the call to ImplPixelToDevicePixel(), since
                // aPosAry already contains the mnOutOff-offsets, they
                // also have to be applied to the region
                Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );

                // TODO: Also respect scaling (that's a bit tricky,
                // since the source points have to move fractional
                // amounts (which is not possible, thus has to be
                // emulated by increases copy area)
                // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
                // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );

                // for now, only identity scales allowed
                if( !aClipRegionBounds.IsEmpty() &&
                    aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
                    aPosAry.mnDestHeight == aPosAry.mnSrcHeight )
                {
                    // now intersect dest rect with clip region
                    aClipRegionBounds.Intersection( Rectangle( aPosAry.mnDestX, 
                                                               aPosAry.mnDestY,
                                                               aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
                                                               aPosAry.mnDestY + aPosAry.mnDestHeight - 1 ) );

                    // Note: I could theoretically optimize away the
                    // DrawBitmap below, if the region is empty
                    // here. Unfortunately, cannot rule out that
                    // somebody relies on the side effects.
                    if( !aClipRegionBounds.IsEmpty() )
                    {
                        aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
                        aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
                        aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
                        aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
                        
                        aPosAry.mnDestX = aClipRegionBounds.Left();
                        aPosAry.mnDestY = aClipRegionBounds.Top();
                        aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
                        aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
                    }
                }

				mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), 
                                        *pMaskBmp->ImplGetSalBitmap(), 
                                        this );
            }
			else
            {
				mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), this );
            }
		}
	}
}
void OutputDevice::ImplDrawAlpha( const Bitmap& rBmp, const AlphaMask& rAlpha,
								  const Point& rDestPt, const Size& rDestSize,
								  const Point& rSrcPtPixel, const Size& rSrcSizePixel )
{
	const Point aNullPt;
	Point		aOutPt( LogicToPixel( rDestPt ) );
	Size        aOutSz( LogicToPixel( rDestSize ) );
	Rectangle	aDstRect( aNullPt, GetOutputSizePixel() );
	const BOOL	bHMirr = aOutSz.Width() < 0, bVMirr = aOutSz.Height() < 0;
#ifdef AVS
	if( OUTDEV_WINDOW == meOutDevType )
	{
		const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() );

		if( !aPaintRgn.IsNull() )
			aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) );
	}
#endif
	if( bHMirr )
	{
		aOutSz.Width() = -aOutSz.Width();
		aOutPt.X() -= ( aOutSz.Width() - 1L );
	}

	if( bVMirr )
	{
		aOutSz.Height() = -aOutSz.Height();
		aOutPt.Y() -= ( aOutSz.Height() - 1L );
	}

	if( !aDstRect.Intersection( Rectangle( aOutPt, aOutSz ) ).IsEmpty() )
	{
        bool bNativeAlpha = false;
        static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA");
        // #i83087# Naturally, system alpha blending cannot work with
        // separate alpha VDev
        if( !pDisableNative && !bHMirr && !bVMirr ) 
        {
            Point aRelPt = aOutPt + Point( mnOutOffX, mnOutOffY );
            SalTwoRect aTR = {
                rSrcPtPixel.X(), rSrcPtPixel.Y(),
                rSrcSizePixel.Width(), rSrcSizePixel.Height(),
                aRelPt.X(), aRelPt.Y(),
                aOutSz.Width(), aOutSz.Height()
            };
            SalBitmap* pSalSrcBmp = rBmp.ImplGetImpBitmap()->ImplGetSalBitmap();
            SalBitmap* pSalAlphaBmp = rAlpha.ImplGetImpBitmap()->ImplGetSalBitmap();
            bNativeAlpha = mpGraphics->DrawAlphaBitmap( aTR, *pSalSrcBmp, *pSalAlphaBmp, this );
        }

        //VirtualDevice* pOldVDev = mpAlphaVDev;

		Rectangle aBmpRect( aNullPt, rBmp.GetSizePixel() );
		if( !bNativeAlpha
                &&  !aBmpRect.Intersection( Rectangle( rSrcPtPixel, rSrcSizePixel ) ).IsEmpty() ) 
		{
			//GDIMetaFile*	pOldMetaFile = mpMetaFile; mpMetaFile = NULL;
			const BOOL		bOldMap = mbMap; mbMap = FALSE;
			Bitmap			aBmp( GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );

            // #109044# The generated bitmap need not necessarily be
            // of aDstRect dimensions, it's internally clipped to
            // window bounds. Thus, we correct the dest size here,
            // since we later use it (in nDstWidth/Height) for pixel
            // access)
            // #i38887# reading from screen may sometimes fail
            if( aBmp.ImplGetImpBitmap() )
                aDstRect.SetSize( aBmp.GetSizePixel() );

			BitmapColor 	aDstCol;
			const long		nSrcWidth = aBmpRect.GetWidth(), nSrcHeight = aBmpRect.GetHeight();
			const long		nDstWidth = aDstRect.GetWidth(), nDstHeight = aDstRect.GetHeight();
			const long		nOutWidth = aOutSz.Width(), nOutHeight = aOutSz.Height();
			const long		nOffX = aDstRect.Left() - aOutPt.X(), nOffY = aDstRect.Top() - aOutPt.Y();
			long			nX, nOutX, nY, nOutY;
			long			nMirrOffX = 0;
			long			nMirrOffY = 0;
			long*			pMapX = new long[ nDstWidth ];
			long*			pMapY = new long[ nDstHeight ];

			// create horizontal mapping table
			if( bHMirr )
				nMirrOffX = ( aBmpRect.Left() << 1 ) + nSrcWidth - 1;

			for( nX = 0L, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
			{
				pMapX[ nX ] = aBmpRect.Left() + nOutX * nSrcWidth / nOutWidth;

				if( bHMirr )
					pMapX[ nX ] = nMirrOffX - pMapX[ nX ];
			}
            
			// create vertical mapping table
			if( bVMirr )
				nMirrOffY = ( aBmpRect.Top() << 1 ) + nSrcHeight - 1;

			for( nY = 0L, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
			{
				pMapY[ nY ] = aBmpRect.Top() + nOutY * nSrcHeight / nOutHeight;

				if( bVMirr )
					pMapY[ nY ] = nMirrOffY - pMapY[ nY ];
			}

            BitmapReadAccess*	pP = ( (Bitmap&) rBmp ).AcquireReadAccess();
            BitmapReadAccess*	pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess();

            //DBG_ASSERT( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
                        //pA->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK, 
                        //"OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" );

            // #i38887# reading from screen may sometimes fail
            if( aBmp.ImplGetImpBitmap() )
            {
                Bitmap aTmp;
#ifdef AVS
                if( mpAlphaVDev )
                {
#endif

                    aTmp = ImplBlendWithAlpha(
                        aBmp,pP,pA,
                        aDstRect,
                        nOffY,nDstHeight,
                        nOffX,nDstWidth,
                        pMapX,pMapY );

#ifdef AVS
                }
                else
                {
                    aTmp = ImplBlend(
                        aBmp,pP,pA,
                        nOffY,nDstHeight,
                        nOffX,nDstWidth,
                        aBmpRect,aOutSz,
                        bHMirr,bVMirr,
                        pMapX,pMapY );
                }

                // #110958# Disable alpha VDev, we're doing the necessary
                // stuff explicitely furher below
                if( mpAlphaVDev )
                    mpAlphaVDev = NULL;
#endif
                DrawBitmap( aDstRect.TopLeft(), 
                            aTmp );
#ifdef AVS
                // #110958# Enable alpha VDev again
                mpAlphaVDev = pOldVDev;
#endif
            }
            
            ( (Bitmap&) rBmp ).ReleaseAccess( pP );
            ( (AlphaMask&) rAlpha ).ReleaseAccess( pA );

			delete[] pMapX;
			delete[] pMapY;
			mbMap = bOldMap;
			//mpMetaFile = pOldMetaFile;
		}
	}
}
// ------------------------------------------------------------------------

Bitmap OutputDevice::ImplBlendWithAlpha( Bitmap              aBmp,
                                         BitmapReadAccess*	 pP,
                                         BitmapReadAccess*	 pA,
                                         const Rectangle&    aDstRect,
                                         const sal_Int32     nOffY, 
                                         const sal_Int32     nDstHeight, 
                                         const sal_Int32     nOffX, 
                                         const sal_Int32     nDstWidth,
                                         const long*         pMapX,
                                         const long*         pMapY )
{
    BitmapColor aDstCol,aSrcCol;
    BYTE        nSrcAlpha, nDstAlpha;
    Bitmap      res;
    int         nX, nOutX, nY, nOutY;
#ifdef QWE
    OSL_ENSURE(mpAlphaVDev,
               "ImplBlendWithAlpha(): call me only with valid alpha VDev!" );

    BOOL bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
    mpAlphaVDev->EnableMapMode(FALSE);

    Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
    BitmapWriteAccess*	pAlphaW = aAlphaBitmap.AcquireWriteAccess();
#endif
    if( GetBitCount() <= 8 )
    {
        Bitmap				aDither( aBmp.GetSizePixel(), 8 );
        BitmapColor 		aIndex( 0 );
        BitmapReadAccess*	pB = aBmp.AcquireReadAccess();
        BitmapWriteAccess*	pW = aDither.AcquireWriteAccess();

        if( pB && pP && pA && pW /*&& pAlphaW*/ )
        {
            for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
            {
                const long nMapY = pMapY[ nY ];
                const long nModY = ( nOutY & 0x0FL ) << 4L;

                for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
                {
                    const long	nMapX = pMapX[ nX ];
                    const ULONG nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];

                    aSrcCol = pP->GetColor( nMapY, nMapX );
                    aDstCol = pB->GetColor( nY, nX );
                    nSrcAlpha  = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex();
                    nDstAlpha  = 0;//255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex();

                    if( nSrcAlpha + nDstAlpha == 0 )
                    {
                        // #i70653# zero alpha -> zero color values
                        aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] +
                                                  nVCLGLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] +
                                                  nVCLBLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] ) );
                    }
                    else
                    {
                        aDstCol.SetRed( (BYTE)(((int)(aSrcCol.GetRed())*nSrcAlpha + (int)(aDstCol.GetRed())*nDstAlpha) /
                                               (nSrcAlpha+nDstAlpha)) );
                        aDstCol.SetGreen( (BYTE)(((int)(aSrcCol.GetGreen())*nSrcAlpha + (int)(aDstCol.GetGreen())*nDstAlpha) /
                                                 (nSrcAlpha+nDstAlpha)) );
                        aDstCol.SetBlue( (BYTE)(((int)(aSrcCol.GetBlue())*nSrcAlpha + (int)(aDstCol.GetBlue())*nDstAlpha) /
                                                (nSrcAlpha+nDstAlpha)) );
                                     
                        aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
                                                  nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
                                                  nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
                    }
                    pW->SetPixel( nY, nX, aIndex );

                    // Have to perform the compositing 'algebra' in
                    // the inverse alpha space (with 255 meaning
                    // opaque), otherwise, transitivity is not
                    // achieved.
                    nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (BYTE)nDstAlpha, nSrcAlpha );

                    aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] +
                                              nVCLGLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] +
                                              nVCLBLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] ) );
#ifdef AVS
                    pAlphaW->SetPixel( nY, nX, aIndex );
#endif
                }
            }
        }

        aBmp.ReleaseAccess( pB );
        aDither.ReleaseAccess( pW );
        res = aDither;
    }
    else
    {
        BitmapWriteAccess*	pB = aBmp.AcquireWriteAccess();
        if( pP && pA && pB )
        {
            for( nY = 0; nY < nDstHeight; nY++ )
            {
                const long	nMapY = pMapY[ nY ];

                for( nX = 0; nX < nDstWidth; nX++ )
                {
                    const long nMapX = pMapX[ nX ];

                    aSrcCol = pP->GetColor( nMapY, nMapX );
                    aDstCol = pB->GetColor( nY, nX );
                    nSrcAlpha  = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex();
                    nDstAlpha  = 255;//255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex();

                    if( nSrcAlpha + nDstAlpha == 0 )
                    {
                        // #i70653# zero alpha -> zero color values
                        aDstCol.SetRed(0);
                        aDstCol.SetGreen(0);
                        aDstCol.SetBlue(0);
                    }
                    else
                    {
                        aDstCol.SetRed( (BYTE)(((int)(aSrcCol.GetRed())*nSrcAlpha + (int)(aDstCol.GetRed())*nDstAlpha) /
                                               (nSrcAlpha+nDstAlpha)) );
                        aDstCol.SetGreen( (BYTE)(((int)(aSrcCol.GetGreen())*nSrcAlpha + (int)(aDstCol.GetGreen())*nDstAlpha) /
                                                 (nSrcAlpha+nDstAlpha)) );
                        aDstCol.SetBlue( (BYTE)(((int)(aSrcCol.GetBlue())*nSrcAlpha + (int)(aDstCol.GetBlue())*nDstAlpha) /
                                                (nSrcAlpha+nDstAlpha)) );
                    }

                    pB->SetPixel( nY, nX, aDstCol );

                    // Have to perform the compositing 'algebra' in
                    // the inverse alpha space (with 255 meaning
                    // opaque), otherwise, transitivity is not
                    // achieved.
                    nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (BYTE)nDstAlpha, nSrcAlpha );
#ifdef AVS
                    pAlphaW->SetPixel( nY, nX, Color(nSrcAlpha, nSrcAlpha, nSrcAlpha) );
#endif
                }
            }
        }

        aBmp.ReleaseAccess( pB );
        res = aBmp;
    }
#ifdef AVS
    aAlphaBitmap.ReleaseAccess( pAlphaW );
    mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
    mpAlphaVDev->EnableMapMode( bOldMapMode );
#endif
    return res;
}

// ------------------------------------------------------------------------

Bitmap OutputDevice::ImplBlend( Bitmap              aBmp,
                                BitmapReadAccess*	pP,
                                BitmapReadAccess*	pA,
                                const sal_Int32     nOffY, 
                                const sal_Int32     nDstHeight, 
                                const sal_Int32     nOffX, 
                                const sal_Int32     nDstWidth,
                                const Rectangle&    aBmpRect, 
                                const Size&         aOutSz,
                                const bool          bHMirr,
                                const bool          bVMirr,
                                const long*         pMapX,
                                const long*         pMapY )
{
    BitmapColor aDstCol;
    Bitmap      res;
    int         nX, nOutX, nY, nOutY;

    if( GetBitCount() <= 8 )
    {
        Bitmap				aDither( aBmp.GetSizePixel(), 8 );
        BitmapColor 		aIndex( 0 );
        BitmapReadAccess*	pB = aBmp.AcquireReadAccess();
        BitmapWriteAccess*	pW = aDither.AcquireWriteAccess();

        if( pB && pP && pA && pW )
        {
            for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
            {
                const long nMapY = pMapY[ nY ];
                const long nModY = ( nOutY & 0x0FL ) << 4L;

                for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
                {
                    const long	nMapX = pMapX[ nX ];
                    const ULONG nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];

                    aDstCol = pB->GetColor( nY, nX );
                    aDstCol.Merge( pP->GetColor( nMapY, nMapX ), (BYTE) pA->GetPixel( nMapY, nMapX ) );
                    aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
                                              nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
                                              nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
                    pW->SetPixel( nY, nX, aIndex );
                }
            }
        }

        aBmp.ReleaseAccess( pB );
        aDither.ReleaseAccess( pW );
        res = aDither;
    }
    else
    {
        BitmapWriteAccess*	pB = aBmp.AcquireWriteAccess();

        bool bFastBlend = false;
#ifdef AVS //fast äåëàòü íå îáÿçàòåëüíî
        if( pP && pA && pB )
        {
            SalTwoRect aTR;
            aTR.mnSrcX      = aBmpRect.Left();
            aTR.mnSrcY      = aBmpRect.Top();
            aTR.mnSrcWidth  = aBmpRect.GetWidth();
            aTR.mnSrcHeight = aBmpRect.GetHeight();
            aTR.mnDestX     = nOffX;
            aTR.mnDestY     = nOffY;
            aTR.mnDestWidth = aOutSz.Width();
            aTR.mnDestHeight= aOutSz.Height();

            if( !bHMirr || !bVMirr )
                bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR );
        }
#endif
        if( pP && pA && pB && !bFastBlend )
        {
            switch( pP->GetScanlineFormat() )
            {
                case( BMP_FORMAT_8BIT_PAL ):
                    {
                        for( nY = 0; nY < nDstHeight; nY++ )
                        {
                            const long	nMapY = pMapY[ nY ];
                            Scanline	pPScan = pP->GetScanline( nMapY );
                            Scanline	pAScan = pA->GetScanline( nMapY );

                            for( nX = 0; nX < nDstWidth; nX++ )
                            {
                                const long nMapX = pMapX[ nX ];
                                aDstCol = pB->GetPixel( nY, nX );
                                pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ),
                                                                     pAScan[ nMapX ] ) );
                            }
                        }
                    }
                    break;

                case( BMP_FORMAT_24BIT_TC_BGR ):
                    {
                        for( nY = 0; nY < nDstHeight; nY++ )
                        {
                            const long	nMapY = pMapY[ nY ];
                            Scanline	pPScan = pP->GetScanline( nMapY );
                            Scanline	pAScan = pA->GetScanline( nMapY );

                            for( nX = 0; nX < nDstWidth; nX++ )
                            {
                                const long	nMapX = pMapX[ nX ];
                                Scanline	pTmp = pPScan + nMapX * 3;

                                aDstCol = pB->GetPixel( nY, nX );
                                pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 2 ], pTmp[ 1 ], pTmp[ 0 ],
                                                                     pAScan[ nMapX ] ) );
                            }
                        }
                    }
                    break;

                case( BMP_FORMAT_24BIT_TC_RGB ):
                    {
                        for( nY = 0; nY < nDstHeight; nY++ )
                        {
                            const long	nMapY = pMapY[ nY ];
                            Scanline	pPScan = pP->GetScanline( nMapY );
                            Scanline	pAScan = pA->GetScanline( nMapY );

                            for( nX = 0; nX < nDstWidth; nX++ )
                            {
                                const long	nMapX = pMapX[ nX ];
                                Scanline    pTmp = pPScan + nMapX * 3;

                                aDstCol = pB->GetPixel( nY, nX );
                                pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 0 ], pTmp[ 1 ], pTmp[ 2 ],
                                                                     pAScan[ nMapX ] ) );
                            }
                        }
                    }
                    break;

                default:
                {
                    for( nY = 0; nY < nDstHeight; nY++ )
                    {
                        const long	nMapY = pMapY[ nY ];
                        Scanline	pAScan = pA->GetScanline( nMapY );

                        for( nX = 0; nX < nDstWidth; nX++ )
                        {
                            const long nMapX = pMapX[ nX ];
                            aDstCol = pB->GetPixel( nY, nX );
                            pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetColor( nMapY, nMapX ),
                                                                 pAScan[ nMapX ] ) );
                        }
                    }
                }
                break;
            }
        }

        aBmp.ReleaseAccess( pB );
        res = aBmp;
    }

    return res;
}
void OutputDevice::DrawMask( const Point& rDestPt,
                                  const Bitmap& rBitmap, const Color& rMaskColor )
{
	const Size aSizePix( rBitmap.GetSizePixel() );
	ImplDrawMask( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, rMaskColor, META_MASK_ACTION );
}
void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
                                  const Bitmap& rBitmap, const Color& rMaskColor )
{
	ImplDrawMask( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, rMaskColor, META_MASKSCALE_ACTION );
}
void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
                                  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
                                  const Bitmap& rBitmap, const Color& rMaskColor )
{
	ImplDrawMask( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor, META_MASKSCALEPART_ACTION );
}
// ------------------------------------------------------------------

void OutputDevice::ImplDrawMask( const Point& rDestPt, const Size& rDestSize,
								 const Point& rSrcPtPixel, const Size& rSrcSizePixel,
								 const Bitmap& rBitmap, const Color& rMaskColor,
								 const ULONG nAction )
{
	if( ROP_INVERT == meRasterOp )
	{
		DrawRect( Rectangle( rDestPt, rDestSize ) );
		return;
	}

	OUTDEV_INIT();

	const ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();
	if ( pImpBmp )
	{
		SalTwoRect aPosAry;

		aPosAry.mnSrcX = rSrcPtPixel.X();
		aPosAry.mnSrcY = rSrcPtPixel.Y();
		aPosAry.mnSrcWidth = rSrcSizePixel.Width();
		aPosAry.mnSrcHeight = rSrcSizePixel.Height();
		aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
		aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
		aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
		aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );

		// spiegeln via Koordinaten wollen wir nicht
		const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, pImpBmp->ImplGetSize() );

		// check if output is necessary
		if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
		{

			if( nMirrFlags )
			{
				Bitmap aTmp( rBitmap );
				aTmp.Mirror( nMirrFlags );
				mpGraphics->DrawMask( &aPosAry, *aTmp.ImplGetImpBitmap()->ImplGetSalBitmap(),
									  ImplColorToSal( rMaskColor ) , this);
			}
			else
				mpGraphics->DrawMask( &aPosAry, *pImpBmp->ImplGetSalBitmap(),
									  ImplColorToSal( rMaskColor ), this );

		}
	}
}
void OutputDevice::DrawGradient( const Rectangle& rRect, const Gradient& rGradient )
{
	Gradient aGradient( rGradient );
    
	// Rechteck in Pixel umrechnen
	Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
	aRect.Justify();

	// Wenn Rechteck leer ist, brauchen wir nichts machen
	if ( !aRect.IsEmpty() )
	{
		// Clip Region sichern
		Push( PUSH_CLIPREGION );
		IntersectClipRegion( rRect );

        // because we draw with no border line, we have to expand gradient
        // rect to avoid missing lines on the right and bottom edge
        aRect.Left()--;
        aRect.Top()--;
        aRect.Right()++;
        aRect.Bottom()++;

		// we need a graphics
		if ( !mpGraphics )
				return;

		if ( mbInitClipRegion )
			ImplInitClipRegion();
	
		if ( !mbOutputClipped )
		{
			// Gradienten werden ohne Umrandung gezeichnet
			if ( mbLineColor || mbInitLineColor )
			{
				mpGraphics->SetLineColor();
				mbInitLineColor = TRUE;
			}
			
			mbInitFillColor = TRUE;

			// calculate step count if neccessary
			if ( !aGradient.GetSteps() )
				aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );

            if( aGradient.GetStyle() == GRADIENT_LINEAR || aGradient.GetStyle() == GRADIENT_AXIAL )
    			ImplDrawLinearGradient( aRect, aGradient, FALSE, NULL );
            else
				ImplDrawComplexGradient( aRect, aGradient, FALSE, NULL );
		}

		Pop();
	}
}
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
{
#ifdef AVS
	if( mbInitClipRegion )
		ImplInitClipRegion();
	
	if( mbOutputClipped )
		return;

	if( !mpGraphics )
			return;

	if( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
	{
		const Rectangle	aRect( rPolyPoly.GetBoundRect() );

		Gradient aGradient( rGradient );

		{
			const PolyPolygon	aPolyPoly( LogicToPixel( rPolyPoly ) );
			const Rectangle		aBoundRect( aPolyPoly.GetBoundRect() );
			Point aPoint;
			Rectangle			aDstRect( aPoint, GetOutputSizePixel() );

			aDstRect.Intersection( aBoundRect );

			if( !aDstRect.IsEmpty() )
			{
				VirtualDevice*	pVDev;
				const Size		aDstSize( aDstRect.GetSize() );

                if( HasAlpha() )
                {
                    // #110958# Pay attention to alpha VDevs here, otherwise, 
                    // background will be wrong: Temp VDev has to have alpha, too.
                    pVDev = new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 );
                }
                else
                {
                    // nothing special here. Plain VDev
                    pVDev = new VirtualDevice();
                }

				if( pVDev->SetOutputSizePixel( aDstSize) )
				{
					MapMode			aVDevMap;
					const BOOL		bOldMap = mbMap;

					EnableMapMode( FALSE );

					pVDev->DrawOutDev( Point(), aDstSize, aDstRect.TopLeft(), aDstSize, *this );
					pVDev->SetRasterOp( ROP_XOR );
					aVDevMap.SetOrigin( Point( -aDstRect.Left(), -aDstRect.Top() ) );
					pVDev->SetMapMode( aVDevMap );
					pVDev->DrawGradient( aBoundRect, aGradient );
					pVDev->SetFillColor( COL_BLACK );
					pVDev->SetRasterOp( ROP_0 );
					pVDev->DrawPolyPolygon( aPolyPoly );
					pVDev->SetRasterOp( ROP_XOR );
					pVDev->DrawGradient( aBoundRect, aGradient );
					aVDevMap.SetOrigin( Point() );
					pVDev->SetMapMode( aVDevMap );
					DrawOutDev( aDstRect.TopLeft(), aDstSize, Point(), aDstSize, *pVDev );

					EnableMapMode( bOldMap );
				}

                delete pVDev;
			}
		}
	}
#endif
}
void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
										   const Gradient& rGradient,
										   BOOL bMtf, const PolyPolygon* pClipPolyPoly )
{
	// rotiertes BoundRect ausrechnen
	Rectangle aRect = rRect;
	aRect.Left()--;
	aRect.Top()--;
	aRect.Right()++;
	aRect.Bottom()++;
	USHORT	nAngle = rGradient.GetAngle() % 3600;
	double	fAngle	= nAngle * F_PI1800;
	double	fWidth	= aRect.GetWidth();
	double	fHeight = aRect.GetHeight();
	double	fDX 	= fWidth  * fabs( cos( fAngle ) ) +
					  fHeight * fabs( sin( fAngle ) );
	double	fDY 	= fHeight * fabs( cos( fAngle ) ) +
					  fWidth  * fabs( sin( fAngle ) );
			fDX 	= (fDX - fWidth)  * 0.5 + 0.5;
			fDY 	= (fDY - fHeight) * 0.5 + 0.5;
	aRect.Left()   -= (long)fDX;
	aRect.Right()  += (long)fDX;
	aRect.Top()    -= (long)fDY;
	aRect.Bottom() += (long)fDY;

	// Rand berechnen und Rechteck neu setzen
	Point		aCenter = rRect.Center();
	Rectangle	aFullRect = aRect;
	long		nBorder = (long)rGradient.GetBorder() * aRect.GetHeight() / 100;
	BOOL		bLinear;

	// Rand berechnen und Rechteck neu setzen fuer linearen Farbverlauf
	if ( rGradient.GetStyle() == GRADIENT_LINEAR )
	{
		bLinear = TRUE;
		aRect.Top() += nBorder;
	}
	// Rand berechnen und Rechteck neu setzen fuer axiale Farbverlauf
	else
	{
		bLinear = FALSE;
		nBorder >>= 1;

		aRect.Top()    += nBorder;
		aRect.Bottom() -= nBorder;
	}

	// Top darf nicht groesser als Bottom sein
	aRect.Top() = Min( aRect.Top(), (long)(aRect.Bottom() - 1) );

	long nMinRect = aRect.GetHeight();

	// Intensitaeten von Start- und Endfarbe ggf. aendern und
	// Farbschrittweiten berechnen
	long			nFactor;
	Color			aStartCol	= rGradient.GetStartColor();
	Color			aEndCol 	= rGradient.GetEndColor();
	long			nStartRed	= aStartCol.GetRed();
	long			nStartGreen = aStartCol.GetGreen();
	long			nStartBlue	= aStartCol.GetBlue();
	long			nEndRed 	= aEndCol.GetRed();
	long			nEndGreen	= aEndCol.GetGreen();
	long			nEndBlue	= aEndCol.GetBlue();
					nFactor 	= rGradient.GetStartIntensity();
					nStartRed	= (nStartRed   * nFactor) / 100;
					nStartGreen = (nStartGreen * nFactor) / 100;
					nStartBlue	= (nStartBlue  * nFactor) / 100;
					nFactor 	= rGradient.GetEndIntensity();
					nEndRed 	= (nEndRed	 * nFactor) / 100;
					nEndGreen	= (nEndGreen * nFactor) / 100;
					nEndBlue	= (nEndBlue  * nFactor) / 100;
	long			nRedSteps	= nEndRed	- nStartRed;
	long			nGreenSteps = nEndGreen - nStartGreen;
	long			nBlueSteps	= nEndBlue	- nStartBlue;
	long            nStepCount = rGradient.GetSteps();

	// Bei nicht linearen Farbverlaeufen haben wir nur die halben Steps
	// pro Farbe
	if ( !bLinear )
	{
		nRedSteps	<<= 1;
		nGreenSteps <<= 1;
		nBlueSteps	<<= 1;
	}

	// Anzahl der Schritte berechnen, falls nichts uebergeben wurde
	if ( !nStepCount )
	{
		long nInc;

		if ( !bMtf )
        {
			nInc = (nMinRect < 50) ? 2 : 4;
        }
		else
        {
            // #105998# Use display-equivalent step size calculation
			nInc = (nMinRect < 800) ? 10 : 20;
        }

		if ( !nInc )
			nInc = 1;

		nStepCount = nMinRect / nInc;
	}
	// minimal drei Schritte und maximal die Anzahl der Farbunterschiede
	long nSteps = Max( nStepCount, 2L );
	long nCalcSteps  = Abs( nRedSteps );
	long nTempSteps = Abs( nGreenSteps );
	if ( nTempSteps > nCalcSteps )
		nCalcSteps = nTempSteps;
	nTempSteps = Abs( nBlueSteps );
	if ( nTempSteps > nCalcSteps )
		nCalcSteps = nTempSteps;
	if ( nCalcSteps < nSteps )
		nSteps = nCalcSteps;
	if ( !nSteps )
		nSteps = 1;

	// Falls axialer Farbverlauf, muss die Schrittanzahl ungerade sein
	if ( !bLinear && !(nSteps & 1) )
		nSteps++;

	// Berechnung ueber Double-Addition wegen Genauigkeit
	double fScanLine = aRect.Top();
	double fScanInc  = (double)aRect.GetHeight() / (double)nSteps;

	// Startfarbe berechnen und setzen
	UINT8	nRed;
	UINT8	nGreen;
	UINT8	nBlue;
	long	nSteps2;
	long	nStepsHalf = 0;
	if ( bLinear )
	{
		// Um 1 erhoeht, um die Border innerhalb der Schleife
		// zeichnen zu koennen
		nSteps2 	= nSteps + 1;
		nRed		= (UINT8)nStartRed;
		nGreen		= (UINT8)nStartGreen;
		nBlue		= (UINT8)nStartBlue;
	}
	else
	{
		// Um 2 erhoeht, um die Border innerhalb der Schleife
		// zeichnen zu koennen
		nSteps2 	= nSteps + 2;
		nRed		= (UINT8)nEndRed;
		nGreen		= (UINT8)nEndGreen;
		nBlue		= (UINT8)nEndBlue;
		nStepsHalf	= nSteps >> 1;
	}

	if ( bMtf )
		;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
	else
		mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );

	// Startpolygon erzeugen (== Borderpolygon)
	Polygon 	aPoly( 4 );
	Polygon 	aTempPoly( 2 );
	aPoly[0] = aFullRect.TopLeft();
	aPoly[1] = aFullRect.TopRight();
	aPoly[2] = aRect.TopRight();
	aPoly[3] = aRect.TopLeft();
	aPoly.Rotate( aCenter, nAngle );

	// Schleife, um rotierten Verlauf zu fuellen
	for ( long i = 0; i < nSteps2; i++ )
	{
		// berechnetesPolygon ausgeben
		if ( bMtf )
			;//mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
		else
			ImplDrawPolygon( aPoly, pClipPolyPoly );

		// neues Polygon berechnen
		aRect.Top() = (long)(fScanLine += fScanInc);

		// unteren Rand komplett fuellen
		if ( i == nSteps )
		{
			aTempPoly[0] = aFullRect.BottomLeft();
			aTempPoly[1] = aFullRect.BottomRight();
		}
		else
		{
			aTempPoly[0] = aRect.TopLeft();
			aTempPoly[1] = aRect.TopRight();
		}
		aTempPoly.Rotate( aCenter, nAngle );

		aPoly[0] = aPoly[3];
		aPoly[1] = aPoly[2];
		aPoly[2] = aTempPoly[1];
		aPoly[3] = aTempPoly[0];

		// Farbintensitaeten aendern...
		// fuer lineare FV
		if ( bLinear )
		{
			nRed	= ImplGetGradientColorValue( nStartRed+((nRedSteps*i)/nSteps2) );
			nGreen	= ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i)/nSteps2) );
			nBlue	= ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i)/nSteps2) );
		}
		// fuer radiale FV
		else
		{
			// fuer axiale FV muss die letzte Farbe der ersten
			// Farbe entsprechen
            // #107350# Setting end color one step earlier, as the
            // last time we get here, we drop out of the loop later
            // on.
			if ( i >= nSteps )
			{
				nRed	= (UINT8)nEndRed;
				nGreen	= (UINT8)nEndGreen;
				nBlue	= (UINT8)nEndBlue;
			}
			else
			{
				if ( i <= nStepsHalf )
				{
					nRed	= ImplGetGradientColorValue( nEndRed-((nRedSteps*i)/nSteps2) );
					nGreen	= ImplGetGradientColorValue( nEndGreen-((nGreenSteps*i)/nSteps2) );
					nBlue	= ImplGetGradientColorValue( nEndBlue-((nBlueSteps*i)/nSteps2) );
				}
				// genau die Mitte und hoeher
				else
				{
					long i2 = i - nStepsHalf;
					nRed	= ImplGetGradientColorValue( nStartRed+((nRedSteps*i2)/nSteps2) );
					nGreen	= ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i2)/nSteps2) );
					nBlue	= ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i2)/nSteps2) );
				}
			}
		}

		if ( bMtf )
			;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
		else
			mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
	}
}

// -----------------------------------------------------------------------

void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
										    const Gradient& rGradient,
										    BOOL bMtf, const PolyPolygon* pClipPolyPoly )
{
	// Feststellen ob Ausgabe ueber Polygon oder PolyPolygon
	// Bei Rasteroperationen ungleich Overpaint immer PolyPolygone,
	// da es zu falschen Ergebnissen kommt, wenn man mehrfach uebereinander
	// ausgibt
	// Bei Druckern auch immer PolyPolygone, da nicht alle Drucker
	// das Uebereinanderdrucken von Polygonen koennen
	// Virtuelle Device werden auch ausgeklammert, da einige Treiber
	// ansonsten zu langsam sind
	PolyPolygon*    pPolyPoly;
	Rectangle	    aRect( rRect );
	Color			aStartCol( rGradient.GetStartColor() );
	Color			aEndCol( rGradient.GetEndColor() );
    long			nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
    long			nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
	long			nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
    long			nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
    long			nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
	long			nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
	long			nRedSteps = nEndRed	- nStartRed;
	long			nGreenSteps = nEndGreen - nStartGreen;
	long			nBlueSteps = nEndBlue	- nStartBlue;
	long            nStepCount = rGradient.GetSteps();
	USHORT	        nAngle = rGradient.GetAngle() % 3600;
	
    if( (meRasterOp != ROP_OVERPAINT) || bMtf )
		pPolyPoly = new PolyPolygon( 2 );
	else
		pPolyPoly = NULL;

    if( rGradient.GetStyle() == GRADIENT_SQUARE || rGradient.GetStyle() == GRADIENT_RECT )
    {
        const double    fAngle	= nAngle * F_PI1800;
	    const double    fWidth	= aRect.GetWidth();
	    const double    fHeight = aRect.GetHeight();
	    double          fDX = fWidth  * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) ); 
	    double          fDY = fHeight * fabs( cos( fAngle ) ) + fWidth  * fabs( sin( fAngle ) ); 

        fDX = ( fDX - fWidth ) * 0.5 + 0.5;
        fDY = ( fDY - fHeight ) * 0.5 + 0.5;
    
	    aRect.Left() -= (long) fDX;
	    aRect.Right() += (long) fDX;
	    aRect.Top() -= (long) fDY;
	    aRect.Bottom() += (long) fDY;
    }
	
    Size aSize( aRect.GetSize() );
	
    if( rGradient.GetStyle() == GRADIENT_RADIAL )
	{
	    // Radien-Berechnung fuer Kreis
		aSize.Width() = (long)(0.5 + sqrt((double)aSize.Width()*(double)aSize.Width() + (double)aSize.Height()*(double)aSize.Height()));
		aSize.Height() = aSize.Width();
	}
	else if( rGradient.GetStyle() == GRADIENT_ELLIPTICAL )
	{
	    // Radien-Berechnung fuer Ellipse
		aSize.Width() = (long)( 0.5 + (double) aSize.Width()  * 1.4142 );
		aSize.Height() = (long)( 0.5 + (double) aSize.Height() * 1.4142 );
	}
    else if( rGradient.GetStyle() == GRADIENT_SQUARE )
    {
		if ( aSize.Width() > aSize.Height() )
			aSize.Height() = aSize.Width();
		else
			aSize.Width() = aSize.Height();
    }

	// neue Mittelpunkte berechnen
	long	nZWidth = aRect.GetWidth()	* (long) rGradient.GetOfsX() / 100;
	long	nZHeight = aRect.GetHeight() * (long) rGradient.GetOfsY() / 100;
	long	nBorderX = (long) rGradient.GetBorder() * aSize.Width()  / 100;
	long	nBorderY = (long) rGradient.GetBorder() * aSize.Height() / 100;
	Point	aCenter( aRect.Left() + nZWidth, aRect.Top() + nZHeight );

	// Rand beruecksichtigen
	aSize.Width() -= nBorderX;
	aSize.Height() -= nBorderY;

	// Ausgaberechteck neu setzen
	aRect.Left() = aCenter.X() - ( aSize.Width() >> 1 );
	aRect.Top() = aCenter.Y() - ( aSize.Height() >> 1 );
    
    aRect.SetSize( aSize );
	long nMinRect = Min( aRect.GetWidth(), aRect.GetHeight() );

	// Anzahl der Schritte berechnen, falls nichts uebergeben wurde
    if( !nStepCount )
	{
		long nInc;

		if ( !bMtf )
        {
			nInc = ( nMinRect < 50 ) ? 2 : 4;
        }
		else
        {
            // #105998# Use display-equivalent step size calculation
			nInc = (nMinRect < 800) ? 10 : 20;
        }

		if( !nInc )
			nInc = 1;

		nStepCount = nMinRect / nInc;
	}
	
    // minimal drei Schritte und maximal die Anzahl der Farbunterschiede
	long nSteps = Max( nStepCount, 2L );
	long nCalcSteps  = Abs( nRedSteps );
	long nTempSteps = Abs( nGreenSteps );
	if ( nTempSteps > nCalcSteps )
		nCalcSteps = nTempSteps;
	nTempSteps = Abs( nBlueSteps );
	if ( nTempSteps > nCalcSteps )
		nCalcSteps = nTempSteps;
	if ( nCalcSteps < nSteps )
		nSteps = nCalcSteps;
	if ( !nSteps )
		nSteps = 1;

	// Ausgabebegrenzungen und Schrittweite fuer jede Richtung festlegen
    Polygon aPoly;
	double  fScanLeft = aRect.Left();
	double  fScanTop = aRect.Top();
	double  fScanRight = aRect.Right();
	double  fScanBottom = aRect.Bottom();
	double  fScanInc = (double) nMinRect / (double) nSteps * 0.5;
    UINT8   nRed = (UINT8) nStartRed, nGreen = (UINT8) nStartGreen, nBlue = (UINT8) nStartBlue;
    bool	bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output

	if( bMtf )
		;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
	else
		mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );

	if( pPolyPoly )
	{   
    	pPolyPoly->Insert( aPoly = rRect );
		pPolyPoly->Insert( aPoly );
	}
	else
    {
	    // extend rect, to avoid missing bounding line
        Rectangle aExtRect( rRect );

        aExtRect.Left() -= 1;
	    aExtRect.Top() -= 1;
	    aExtRect.Right() += 1;
	    aExtRect.Bottom() += 1;

		ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
    }

	// Schleife, um nacheinander die Polygone/PolyPolygone auszugeben
	for( long i = 1; i < nSteps; i++ )
	{
		// neues Polygon berechnen
		aRect.Left() = (long)( fScanLeft += fScanInc );
		aRect.Top() = (long)( fScanTop += fScanInc );
		aRect.Right() = (long)( fScanRight -= fScanInc );
		aRect.Bottom() = (long)( fScanBottom -= fScanInc );

		if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
			break;

        if( rGradient.GetStyle() == GRADIENT_RADIAL || rGradient.GetStyle() == GRADIENT_ELLIPTICAL )
		    aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
        else
    		aPoly = Polygon( aRect );

		aPoly.Rotate( aCenter, nAngle );

		// Farbe entsprechend anpassen
        const long nStepIndex = ( ( pPolyPoly != NULL ) ? i : ( i + 1 ) );
        nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
		nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
		nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );

		// entweder langsame PolyPolygon-Ausgaben oder schnelles Polygon-Painting
		if( pPolyPoly )
		{
            bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output

			pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
			pPolyPoly->Replace( aPoly, 1 );

			if( bMtf )
				;//mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
			else
				ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );

            // #107349# Set fill color _after_ geometry painting:
            // pPolyPoly's geometry is the band from last iteration's
            // aPoly to current iteration's aPoly. The window outdev
            // path (see else below), on the other hand, paints the
            // full aPoly. Thus, here, we're painting the band before
            // the one painted in the window outdev path below. To get
            // matching colors, have to delay color setting here.
            if( bMtf )
                ;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
            else
                mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
		}
		else
        {
            // #107349# Set fill color _before_ geometry painting
            if( bMtf )
                ;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
            else
                mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );

			ImplDrawPolygon( aPoly, pClipPolyPoly );
        }
	}

	// Falls PolyPolygon-Ausgabe, muessen wir noch ein letztes inneres Polygon zeichnen
	if( pPolyPoly )
	{
		const Polygon& rPoly = pPolyPoly->GetObject( 1 );
		
        if( !rPoly.GetBoundRect().IsEmpty() )
		{
            // #107349# Paint last polygon with end color only if loop
            // has generated output. Otherwise, the current
            // (i.e. start) color is taken, to generate _any_ output.
            if( bPaintLastPolygon )
            {
                nRed = ImplGetGradientColorValue( nEndRed );
                nGreen = ImplGetGradientColorValue( nEndGreen );
                nBlue = ImplGetGradientColorValue( nEndBlue );
            }

    		if( bMtf )
            {
	    		;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
				;//mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
            }
		    else
            {
			    mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
   				ImplDrawPolygon( rPoly, pClipPolyPoly );
            }
		}

		delete pPolyPoly;
	}
}
void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const ::Hatch& rHatch )
{
	Hatch aHatch( rHatch );

	if( !mpGraphics )
		return;

	if( mbInitClipRegion )
		ImplInitClipRegion();

	if( mbOutputClipped )
		return;

	if( rPolyPoly.Count() )
	{ 
		PolyPolygon		aPolyPoly( LogicToPixel( rPolyPoly ) );
		BOOL			bOldMap = mbMap;

		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
        aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) );

		EnableMapMode( FALSE );
		Push( PUSH_LINECOLOR );
		SetLineColor( aHatch.GetColor() );
		ImplInitLineColor();
		ImplDrawHatch( aPolyPoly, aHatch, FALSE );
		Pop();
		EnableMapMode( bOldMap );
	}
}
void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, BOOL bMtf )
{
	Rectangle	aRect( rPolyPoly.GetBoundRect() );
	const long	nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 );
	const long	nWidth = ImplDevicePixelToLogicWidth( Max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) );
	Point*		pPtBuffer = new Point[ HATCH_MAXPOINTS ];
	Point		aPt1, aPt2, aEndPt1;
	Size		aInc;

	// Single hatch
	aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth;
	ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
	do
	{
		ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
		aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
		aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
	}
	while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );

	if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) )
	{
		// Double hatch
		ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 );
		do
		{
			ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
			aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
			aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
		}
		while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );

		if( rHatch.GetStyle() == HATCH_TRIPLE )
		{
			// Triple hatch
			ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 );
			do
			{
				ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
				aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
				aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
			}
			while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
		}
	}

	delete[] pPtBuffer;
}

// -----------------------------------------------------------------------
void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, USHORT nAngle10,
										Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
{
	Point	aRef;
	long	nAngle = nAngle10 % 1800;
	long	nOffset = 0;

	if( nAngle > 900 )
		nAngle -= 1800;

	aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );

	if( 0 == nAngle )
	{
		rInc = Size( 0, nDist );
		rPt1 = rRect.TopLeft();
		rPt2 = rRect.TopRight();
		rEndPt1 = rRect.BottomLeft();

		if( aRef.Y() <= rRect.Top() )
			nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
		else
			nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );

		rPt1.Y() -= nOffset;
		rPt2.Y() -= nOffset;
	}
	else if( 900 == nAngle )
	{
		rInc = Size( nDist, 0 );
		rPt1 = rRect.TopLeft();
		rPt2 = rRect.BottomLeft();
		rEndPt1 = rRect.TopRight();

		if( aRef.X() <= rRect.Left() )
			nOffset = ( rRect.Left() - aRef.X() ) % nDist;
		else
			nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );

		rPt1.X() -= nOffset;
		rPt2.X() -= nOffset;
	}
	else if( nAngle >= -450 && nAngle <= 450 )
	{
		const double	fAngle = F_PI1800 * labs( nAngle );
		const double	fTan = tan( fAngle );
		const long		nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan );
		long			nPY;

		rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) );

		if( nAngle > 0 )
		{
			rPt1 = rRect.TopLeft();
			rPt2 = Point( rRect.Right(), rRect.Top() - nYOff );
			rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff );
			nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) );
		}
		else
		{
			rPt1 = rRect.TopRight();
			rPt2 = Point( rRect.Left(), rRect.Top() - nYOff );
			rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff );
			nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) );
		}

		if( nPY <= rPt1.Y() )
			nOffset = ( rPt1.Y() - nPY ) % nDist;
		else
			nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );

		rPt1.Y() -= nOffset;
		rPt2.Y() -= nOffset;
	}
	else
	{
		const double fAngle = F_PI1800 * labs( nAngle );
		const double fTan = tan( fAngle );
		const long	 nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan );
		long		 nPX;

		rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 );

		if( nAngle > 0 )
		{
			rPt1 = rRect.TopLeft();
			rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() );
			rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() );
			nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
		}
		else
		{
			rPt1 = rRect.BottomLeft();
			rPt2 = Point( rRect.Left() - nXOff, rRect.Top() );
			rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() );
			nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
		}

		if( nPX <= rPt1.X() )
			nOffset = ( rPt1.X() - nPX ) % nDist;
		else
			nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );

		rPt1.X() -= nOffset;
		rPt2.X() -= nOffset;
	}
}
// ------------------------------------------------------------------------
void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly,
									  Point* pPtBuffer, BOOL bMtf )
{
	double	fX, fY;
	long	nAdd, nPCounter = 0;

	for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
	{
		const Polygon& rPoly = rPolyPoly[ (USHORT) nPoly ];

		if( rPoly.GetSize() > 1 )
		{
			Line	aCurSegment( rPoly[ 0 ], Point() );

			for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
			{
				aCurSegment.SetEnd( rPoly[ (USHORT)( i % nCount ) ] );
				nAdd = 0;

				if( rLine.Intersection( aCurSegment, fX, fY ) )
				{
					if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) && 
						( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
					{
						const Line		aPrevSegment( rPoly[ (USHORT)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() );
						const double	fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() );
						const double	fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() );

						if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) || 
							( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
						{
							nAdd = 1;
						}
					}
					else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) && 
							 ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
					{
						const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (USHORT)( ( i + 1 ) % nCount ) ] );

						if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) && 
							( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
						{
							nAdd = 1;
						}
					}
					else
						nAdd = 1;

					if( nAdd )
						pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
				}

				aCurSegment.SetStart( aCurSegment.GetEnd() );
			}
		}
	}

	if( nPCounter > 1 )
	{
		qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc );

		if( nPCounter & 1 )
			nPCounter--;

		if( bMtf )
		{
#ifdef AVS
			for( long i = 0; i < nPCounter; i += 2 )
				mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
#endif
		}
		else
		{
			for( long i = 0; i < nPCounter; i += 2 )
			{
#ifdef AVS
                if( mpPDFWriter )
                {
                    mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] );
                }
                else
                {
#endif
                    const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) );
                    const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) );
                    mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this );
#ifdef AVS
                }
#endif
			}
		}
	}
}

void OutputDevice::DrawWallpaper( const Rectangle& rRect, const Wallpaper& rWallpaper )
{
	if ( rWallpaper.GetStyle() != WALLPAPER_NULL )
	{
		Rectangle aRect = LogicToPixel( rRect );
		aRect.Justify();

		if ( !aRect.IsEmpty() )
		{
			ImplDrawWallpaper( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(),
							   rWallpaper );
		}
	}
}

void OutputDevice::ImplDrawWallpaper( long nX, long nY,
									  long nWidth, long nHeight,
									  const Wallpaper& rWallpaper )
{
	if( rWallpaper.IsBitmap() )
		ImplDrawBitmapWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
	else if( rWallpaper.IsGradient() )
		ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
	else
		ImplDrawColorWallpaper(  nX, nY, nWidth, nHeight, rWallpaper );
}
void OutputDevice::ImplDrawBitmapWallpaper( long nX, long nY,
											long nWidth, long nHeight,
											const Wallpaper& rWallpaper )
{
	BitmapEx				aBmpEx;
	const BitmapEx* 		pCached = rWallpaper.ImplGetImpWallpaper()->ImplGetCachedBitmap();
	Point					aPos;
	Size					aSize;
	const WallpaperStyle	eStyle = rWallpaper.GetStyle();
	const BOOL				bOldMap = mbMap;
	BOOL					bDrawn = FALSE;
	BOOL					bDrawGradientBackground = FALSE;
	BOOL					bDrawColorBackground = FALSE;

	if( pCached )
		aBmpEx = *pCached;
	else
		aBmpEx = rWallpaper.GetBitmap();

	const long nBmpWidth = aBmpEx.GetSizePixel().Width();
	const long nBmpHeight = aBmpEx.GetSizePixel().Height();
	const BOOL bTransparent = aBmpEx.IsTransparent();

	// draw background
	if( bTransparent )
	{
		if( rWallpaper.IsGradient() )
			bDrawGradientBackground = TRUE;
		else
		{
			if( !pCached && !rWallpaper.GetColor().GetTransparency() )
			{
				VirtualDevice aVDev( *this );
				aVDev.SetBackground( rWallpaper.GetColor() );
				aVDev.SetOutputSizePixel( Size( nBmpWidth, nBmpHeight ) );
				aVDev.DrawBitmapEx( Point(), aBmpEx );
				aBmpEx = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
			}

			bDrawColorBackground = TRUE;
		}
	}
	else if( eStyle != WALLPAPER_TILE && eStyle != WALLPAPER_SCALE )
	{
		if( rWallpaper.IsGradient() )
			bDrawGradientBackground = TRUE;
		else
			bDrawColorBackground = TRUE;
	}

	// background of bitmap?
	if( bDrawGradientBackground )
		ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
	else if( bDrawColorBackground && bTransparent )
	{
		ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
		bDrawColorBackground = FALSE;
	}

	// calc pos and size
	if( rWallpaper.IsRect() )
	{
		const Rectangle aBound( LogicToPixel( rWallpaper.GetRect() ) );
		aPos = aBound.TopLeft();
		aSize = aBound.GetSize();
	}
	else
	{
		aPos = Point( nX, nY );
		aSize = Size( nWidth, nHeight );
	}

	EnableMapMode( FALSE );
	Push( PUSH_CLIPREGION );
	IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );

	switch( eStyle )
	{
		case( WALLPAPER_SCALE ):
		{
			if( !pCached || ( pCached->GetSizePixel() != aSize ) )
			{
				if( pCached )
					rWallpaper.ImplGetImpWallpaper()->ImplReleaseCachedBitmap();

				aBmpEx = rWallpaper.GetBitmap();
				aBmpEx.Scale( aSize );
				aBmpEx = BitmapEx( aBmpEx.GetBitmap().CreateDisplayBitmap( this ), aBmpEx.GetMask() );
			}
		}
		break;

		case( WALLPAPER_TOPLEFT ):
		break;

		case( WALLPAPER_TOP ):
			aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
		break;

		case( WALLPAPER_TOPRIGHT ):
			aPos.X() += ( aSize.Width() - nBmpWidth );
		break;

		case( WALLPAPER_LEFT ):
			aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
		break;

		case( WALLPAPER_CENTER ):
		{
			aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
			aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
		}
		break;

		case( WALLPAPER_RIGHT ):
		{
			aPos.X() += ( aSize.Width() - nBmpWidth );
			aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
		}
		break;

		case( WALLPAPER_BOTTOMLEFT ):
			aPos.Y() += ( aSize.Height() - nBmpHeight );
		break;

		case( WALLPAPER_BOTTOM ):
		{
			aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
			aPos.Y() += ( aSize.Height() - nBmpHeight );
		}
		break;

		case( WALLPAPER_BOTTOMRIGHT ):
		{
			aPos.X() += ( aSize.Width() - nBmpWidth );
			aPos.Y() += ( aSize.Height() - nBmpHeight );
		}
		break;

		default:
		{
			const long	nRight = nX + nWidth - 1L;
			const long	nBottom = nY + nHeight - 1L;
			long		nFirstX;
			long		nFirstY;

			if( eStyle == WALLPAPER_TILE )
			{
				nFirstX = aPos.X();
				nFirstY = aPos.Y();
			}
			else
			{
				nFirstX = aPos.X() + ( ( aSize.Width() - nBmpWidth ) >> 1 );
				nFirstY = aPos.Y() + ( ( aSize.Height() - nBmpHeight ) >> 1 );
			}

			const long	nOffX = ( nFirstX - nX ) % nBmpWidth;
			const long	nOffY = ( nFirstY - nY ) % nBmpHeight;
			long		nStartX = nX + nOffX;
			long		nStartY = nY + nOffY;

			if( nOffX > 0L )
				nStartX -= nBmpWidth;

			if( nOffY > 0L )
				nStartY -= nBmpHeight;

			for( long nBmpY = nStartY; nBmpY <= nBottom; nBmpY += nBmpHeight )
				for( long nBmpX = nStartX; nBmpX <= nRight; nBmpX += nBmpWidth )
					DrawBitmapEx( Point( nBmpX, nBmpY ), aBmpEx );

			bDrawn = TRUE;
		}
		break;
	}

	if( !bDrawn )
	{
		// optimized for non-transparent bitmaps
		if( bDrawColorBackground )
		{
			const Size		aBmpSize( aBmpEx.GetSizePixel() );
			const Point 	aTmpPoint;
			const Rectangle aOutRect( aTmpPoint, GetOutputSizePixel() );
			const Rectangle aColRect( Point( nX, nY ), Size( nWidth, nHeight ) );
			Rectangle		aWorkRect;

			aWorkRect = Rectangle( 0, 0, aOutRect.Right(), aPos.Y() - 1L );
			aWorkRect.Justify();
			aWorkRect.Intersection( aColRect );
			if( !aWorkRect.IsEmpty() )
			{
				ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
										aWorkRect.GetWidth(), aWorkRect.GetHeight(),
										rWallpaper );
			}

			aWorkRect = Rectangle( 0, aPos.Y(), aPos.X() - 1L, aPos.Y() + aBmpSize.Height() - 1L );
			aWorkRect.Justify();
			aWorkRect.Intersection( aColRect );
			if( !aWorkRect.IsEmpty() )
			{
				ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
										aWorkRect.GetWidth(), aWorkRect.GetHeight(),
										rWallpaper );
			}

			aWorkRect = Rectangle( aPos.X() + aBmpSize.Width(), aPos.Y(), aOutRect.Right(), aPos.Y() + aBmpSize.Height() - 1L );
			aWorkRect.Justify();
			aWorkRect.Intersection( aColRect );
			if( !aWorkRect.IsEmpty() )
			{
				ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
										aWorkRect.GetWidth(), aWorkRect.GetHeight(),
										rWallpaper );
			}

			aWorkRect = Rectangle( 0, aPos.Y() + aBmpSize.Height(), aOutRect.Right(), aOutRect.Bottom() );
			aWorkRect.Justify();
			aWorkRect.Intersection( aColRect );
			if( !aWorkRect.IsEmpty() )
			{
				ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
										aWorkRect.GetWidth(), aWorkRect.GetHeight(),
										rWallpaper );
			}
		}

		DrawBitmapEx( aPos, aBmpEx );
	}

	rWallpaper.ImplGetImpWallpaper()->ImplSetCachedBitmap( aBmpEx );

	Pop();
	EnableMapMode( bOldMap );
}

void OutputDevice::ImplDrawGradientWallpaper( long nX, long nY,
											  long nWidth, long nHeight,
											  const Wallpaper& rWallpaper )
{
	Rectangle		aBound;
	const BOOL		bOldMap = mbMap;
    BOOL            bNeedGradient = TRUE;

	if ( rWallpaper.IsRect() )
		aBound = LogicToPixel( rWallpaper.GetRect() );
	else
		aBound = Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );

	EnableMapMode( FALSE );
	Push( PUSH_CLIPREGION );
	IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );

    if( bNeedGradient )
	    DrawGradient( aBound, rWallpaper.GetGradient() );

	Pop();
	EnableMapMode( bOldMap );
}
void OutputDevice::ImplDrawColorWallpaper( long nX, long nY,
										   long nWidth, long nHeight,
										   const Wallpaper& rWallpaper )
{
	// Wallpaper ohne Umrandung zeichnen
	Color aOldLineColor = GetLineColor();
	Color aOldFillColor = GetFillColor();
	SetLineColor();
	SetFillColor( rWallpaper.GetColor() );
    BOOL bMap = mbMap;
    EnableMapMode( FALSE );
    DrawRect( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );
	SetLineColor( aOldLineColor );
	SetFillColor( aOldFillColor );
    EnableMapMode( bMap );
}
void OutputDevice::DrawTransparent( const PolyPolygon& rPolyPoly, USHORT nTransparencePercent )
{
    bool bDrawn = false;
	// short circuit for drawing an opaque polygon
	if( (nTransparencePercent < 1) )
	{
		DrawPolyPolygon( rPolyPoly );
		return;
	}

	// short circut for drawing an invisible polygon
	if( !mbFillColor || (nTransparencePercent >= 100) )
	{
		// short circuit if the polygon border is invisible too
		if( !mbLineColor )
			return;

		// DrawTransparent() assumes that the border is NOT to be drawn transparently???
		Push( PUSH_FILLCOLOR );
		SetFillColor();
		DrawPolyPolygon( rPolyPoly );
		Pop();
		return;
	}


	// get the device graphics as drawing target
	if( !mpGraphics )
			return;

    // debug helper:
    static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA");

	if( 1 )
	{

		{
 			PolyPolygon 	aPolyPoly( LogicToPixel( rPolyPoly ) );
			Rectangle		aPolyRect( aPolyPoly.GetBoundRect() );
			Point			aPoint;
			Rectangle		aDstRect( aPoint, GetOutputSizePixel() );

			aDstRect.Intersection( aPolyRect );

			if( !aDstRect.IsEmpty() )
			{
                // #i66849# Added fast path for exactly rectangular
                // polygons
                // #i83087# Naturally, system alpha blending cannot
                // work with separate alpha VDev
                if( !pDisableNative && aPolyPoly.IsRect() )
                {
                    // setup Graphics only here (other cases delegate
                    // to basic OutDev methods)
                    if( 1 )
                    {
                        if ( mbInitClipRegion )
                            ImplInitClipRegion();        
                        if ( mbInitLineColor )
                            ImplInitLineColor();
                        if ( mbInitFillColor )
                            ImplInitFillColor();
    
                        Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
                        Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );

                        if( !mbOutputClipped )
                        {
                            bDrawn = mpGraphics->DrawAlphaRect( 
                               aPixelRect.Left(), aPixelRect.Top(), 
                               // #i98405# use methods with small g, else one pixel too much will be painted.
                               // This is because the source is a polygon which when painted would not paint
                               // the rightmost and lowest pixel line(s), so use one pixel less for the 
                               // rectangle, too.
                               aPixelRect.getWidth(), aPixelRect.getHeight(),
                               static_cast<sal_uInt8>(nTransparencePercent), 
                               this );
                        }
                        else
                            bDrawn = true;
                    }
                }

                if( !bDrawn )
                {
                    //VirtualDevice	aVDev( *this, 1 );
                    VirtualDevice	aVDev( *this );
                    const Size		aDstSz( aDstRect.GetSize() );
                    const BYTE		cTrans = (BYTE) MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 );

                    if( aDstRect.Left() || aDstRect.Top() )
                        aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );

                    if( aVDev.SetOutputSizePixel( aDstSz ) )
                    {
                        const BOOL bOldMap = mbMap;

                        EnableMapMode( FALSE );

                        aVDev.SetLineColor( COL_BLACK );
                        aVDev.SetFillColor( COL_BLACK );
                        aVDev.DrawPolyPolygon( aPolyPoly );

                        Bitmap				aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
                        Bitmap				aPolyMask( aVDev.GetBitmap( Point(), aDstSz ) );

                        // #107766# check for non-empty bitmaps before accessing them
                        if( !!aPaint && !!aPolyMask )
                        {
                            BitmapWriteAccess*	pW = aPaint.AcquireWriteAccess();
                            BitmapReadAccess*	pR = aPolyMask.AcquireReadAccess();

                            if( pW && pR )
                            {
                                BitmapColor 		aPixCol;
                                const BitmapColor	aFillCol( GetFillColor() );
                                const BitmapColor	aWhite( pR->GetBestMatchingColor( Color( COL_WHITE ) ) );
                                const BitmapColor	aBlack( pR->GetBestMatchingColor( Color( COL_BLACK ) ) );
                                const long			nWidth = pW->Width(), nHeight = pW->Height();
                                const long			nR = aFillCol.GetRed(), nG = aFillCol.GetGreen(), nB = aFillCol.GetBlue();
                                long				nX, nY;

                                if( aPaint.GetBitCount() <= 8 )
                                {
                                    const BitmapPalette&	rPal = pW->GetPalette();
                                    const USHORT			nCount = rPal.GetEntryCount();
                                    BitmapColor*			pMap = (BitmapColor*) new BYTE[ nCount * sizeof( BitmapColor ) ];

                                    for( USHORT i = 0; i < nCount; i++ )
                                    {
                                        BitmapColor aCol( rPal[ i ] );
                                        pMap[ i ] = BitmapColor( (BYTE) rPal.GetBestIndex( aCol.Merge( aFillCol, cTrans ) ) );
                                    }

                                    if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
                                        pW->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
                                    {
                                        const BYTE cBlack = aBlack.GetIndex();

                                        for( nY = 0; nY < nHeight; nY++ )
                                        {
                                            Scanline	pWScan = pW->GetScanline( nY );
                                            Scanline	pRScan = pR->GetScanline( nY );
                                            BYTE		cBit = 128;

                                            for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
                                            {
                                                if( !cBit )
                                                    cBit = 128, pRScan++;

                                                if( ( *pRScan & cBit ) == cBlack )
                                                    *pWScan = (BYTE) pMap[ *pWScan ].GetIndex();
                                            }
                                        }
                                    }
                                    else
                                    {
                                        for( nY = 0; nY < nHeight; nY++ )
                                            for( nX = 0; nX < nWidth; nX++ )
                                                if( pR->GetPixel( nY, nX ) == aBlack )
                                                    pW->SetPixel( nY, nX, pMap[ pW->GetPixel( nY, nX ).GetIndex() ] );
                                    }

                                    delete[] (BYTE*) pMap;
                                }
                                else
                                {
                                    if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
                                        pW->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR )
                                    {
                                        const BYTE cBlack = aBlack.GetIndex();

                                        for( nY = 0; nY < nHeight; nY++ )
                                        {
                                            Scanline	pWScan = pW->GetScanline( nY );
                                            Scanline	pRScan = pR->GetScanline( nY );
                                            BYTE		cBit = 128;

                                            for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
                                            {
                                                if( !cBit )
                                                    cBit = 128, pRScan++;

                                                if( ( *pRScan & cBit ) == cBlack )
                                                {
                                                    pWScan[ 0 ] = COLOR_CHANNEL_MERGE( pWScan[ 0 ], nB, cTrans );
                                                    pWScan[ 1 ] = COLOR_CHANNEL_MERGE( pWScan[ 1 ], nG, cTrans );
                                                    pWScan[ 2 ] = COLOR_CHANNEL_MERGE( pWScan[ 2 ], nR, cTrans );
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        for( nY = 0; nY < nHeight; nY++ )
                                        {
                                            for( nX = 0; nX < nWidth; nX++ )
                                            {
                                                if( pR->GetPixel( nY, nX ) == aBlack )
                                                {
                                                    aPixCol = pW->GetColor( nY, nX );
                                                    pW->SetPixel( nY, nX, aPixCol.Merge( aFillCol, cTrans ) );
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            aPolyMask.ReleaseAccess( pR );
                            aPaint.ReleaseAccess( pW );

                            DrawBitmap( aDstRect.TopLeft(), aPaint );

                            EnableMapMode( bOldMap );

                            if( mbLineColor )
                            {
                                Push( PUSH_FILLCOLOR );
                                SetFillColor();
                                DrawPolyPolygon( rPolyPoly );
                                Pop();
                            }
                        }
                    }
                    else
                        DrawPolyPolygon( rPolyPoly );
                }
            }
        }
	}
}
void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos, const Size& rSize, const Gradient& rTransparenceGradient )
{
	const Color aBlack( COL_BLACK );

	if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) )
	{
		( (GDIMetaFile&) rMtf ).WindStart();
		( (GDIMetaFile&) rMtf ).Play( this, rPos, rSize );
		( (GDIMetaFile&) rMtf ).WindStart();
	}
	else
	{
		Rectangle		aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) );
		Point			aPoint;
		Rectangle		aDstRect( aPoint, GetOutputSizePixel() );

		aDstRect.Intersection( aOutRect );

		if( !aDstRect.IsEmpty() )
		{
			VirtualDevice* pVDev = new VirtualDevice;

			((OutputDevice*)pVDev)->mnDPIX = mnDPIX;
			((OutputDevice*)pVDev)->mnDPIY = mnDPIY;

			if( pVDev->SetOutputSizePixel( aDstRect.GetSize() ) )
			{
				Bitmap		aPaint, aMask;
				AlphaMask	aAlpha;
				MapMode 	aMap( GetMapMode() );
				Point		aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
				const BOOL	bOldMap = mbMap;

				aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
				pVDev->SetMapMode( aMap );
				const BOOL	bVDevOldMap = pVDev->IsMapModeEnabled();

				// create paint bitmap
				( (GDIMetaFile&) rMtf ).WindStart();
				( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize );
				( (GDIMetaFile&) rMtf ).WindStart();
				pVDev->EnableMapMode( FALSE );
				aPaint = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );
				pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( TRUE ) here!

				// create mask bitmap
				pVDev->SetLineColor( COL_BLACK );
				pVDev->SetFillColor( COL_BLACK );
				pVDev->DrawRect( Rectangle( pVDev->PixelToLogic( Point() ), pVDev->GetOutputSize() ) );
#ifdef QWE
				pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT |
									DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT );
#endif
				( (GDIMetaFile&) rMtf ).WindStart();
				( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize );
				( (GDIMetaFile&) rMtf ).WindStart();
				pVDev->EnableMapMode( FALSE );
				aMask = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );
				pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( TRUE ) here!
#ifdef QWE
				// create alpha mask from gradient
				pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT );
#endif
				pVDev->DrawGradient( Rectangle( rPos, rSize ), rTransparenceGradient );
#ifdef QWE
				pVDev->SetDrawMode( DRAWMODE_DEFAULT );
#endif
				pVDev->EnableMapMode( FALSE );
				pVDev->DrawMask( Point(), pVDev->GetOutputSizePixel(), aMask, Color( COL_WHITE ) );

				aAlpha = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );

				delete pVDev;

				EnableMapMode( FALSE );
				DrawBitmapEx( aDstRect.TopLeft(), BitmapEx( aPaint, aAlpha ) );
				EnableMapMode( bOldMap );
			}
			else
				delete pVDev;
		}
	}
}

void OutputDevice::DrawEPS( const Point& rPoint, const Size& rSize,
							const GfxLink& rGfxLink, GDIMetaFile* pSubst )
{
	if( mbOutputClipped )
		return;

	Rectangle	aRect( ImplLogicToDevicePixel( Rectangle( rPoint, rSize ) ) );
	if( !aRect.IsEmpty() )
	{
        // else draw the substitution graphics
		if( pSubst )
		{
			//Graphic( *pSubst ).Draw( this, rPoint, rSize );
			Gradient rTransparenceGradient;
			rTransparenceGradient.SetStartColor( Color( COL_BLACK ) );
			rTransparenceGradient.SetEndColor( Color( COL_BLACK ) );
			DrawTransparent( *pSubst, rPoint, rSize, rTransparenceGradient );
		}

	}
}
// -----------------------------------------------------------------------

bool OutputDevice::ImplIsAntiparallel() const
{
    bool bRet = false;
    if( mpGraphics )
    {
        if( ( (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && ! IsRTLEnabled() ) ||
            ( ! (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && IsRTLEnabled() ) )
        {
            bRet = true;
        }
    }
    return bRet;
}
void OutputDevice::ImplSetClipRegion( const Region* pRegion )
{
	if ( !pRegion )
	{
		if ( mbClipRegion )
		{
			maRegion			= Region( REGION_NULL );
			mbClipRegion		= FALSE;
			mbInitClipRegion	= TRUE;
		}
	}
	else
	{
		maRegion			= *pRegion;
		mbClipRegion		= TRUE;
		mbInitClipRegion	= TRUE;
	}
}
void OutputDevice::SetClipRegion()
{
	ImplSetClipRegion( NULL );
}
void OutputDevice::SetClipRegion( const Region& rRegion )
{
	if ( rRegion.GetType() == REGION_NULL )
		ImplSetClipRegion( NULL );
	else
	{
		Region aRegion = LogicToPixel( rRegion );
		ImplSetClipRegion( &aRegion );
	}
}
void OutputDevice::MoveClipRegion( long nHorzMove, long nVertMove )
{
	if ( mbClipRegion )
	{
		maRegion.Move( ImplLogicWidthToDevicePixel( nHorzMove ),
					   ImplLogicHeightToDevicePixel( nVertMove ) );
		mbInitClipRegion = TRUE;
	}
}
void OutputDevice::IntersectClipRegion( const Rectangle& rRect )
{
	Rectangle aRect = LogicToPixel( rRect );
	maRegion.Intersect( aRect );
	mbClipRegion		= TRUE;
	mbInitClipRegion	= TRUE;
}
void OutputDevice::IntersectClipRegion( const Region& rRegion )
{
	RegionType eType = rRegion.GetType();
	if ( eType != REGION_NULL )
	{
		Region aRegion = LogicToPixel( rRegion );
		maRegion.Intersect( aRegion );
		mbClipRegion		= TRUE;
		mbInitClipRegion	= TRUE;
	}
}
// -----------------------------------------------------------------------
Region OutputDevice::GetClipRegion() const
{
	return PixelToLogic( maRegion );
}
void OutputDevice::SetLineColor()
{
	if ( mbLineColor )
	{
		mbInitLineColor = TRUE;
		mbLineColor = FALSE;
		maLineColor = Color( COL_TRANSPARENT );
	}
}
void OutputDevice::SetLineColor( const Color& rColor )
{
	if( rColor.GetTransparency() )
	{
		if ( mbLineColor )
		{
			mbInitLineColor = TRUE;
			mbLineColor = FALSE;
			maLineColor = Color( COL_TRANSPARENT );
		}
	}
	else
	{
		if( maLineColor != rColor )
		{
			mbInitLineColor = TRUE;
			mbLineColor = TRUE;
			maLineColor = rColor;
		}
	}
}
void OutputDevice::SetFillColor()
{
	if ( mbFillColor )
	{
		mbInitFillColor = TRUE;
		mbFillColor = FALSE;
		maFillColor = Color( COL_TRANSPARENT );
	}
}
void OutputDevice::SetFillColor( const Color& rColor )
{
	if ( rColor.GetTransparency() )
	{
		if ( mbFillColor )
		{
			mbInitFillColor = TRUE;
			mbFillColor = FALSE;
			maFillColor = Color( COL_TRANSPARENT );
		}
	}
	else
	{
		if ( maFillColor != rColor )
		{
			mbInitFillColor = TRUE;
			mbFillColor = TRUE;
			maFillColor = rColor;
		}
	}
}
void OutputDevice::SetTextColor( const Color& rColor )
{
    if ( maTextColor != rColor )
    {
        maTextColor = rColor;
        mbInitTextColor = TRUE;
    }
}
void OutputDevice::SetTextFillColor()
{
    if ( maFont.GetColor() != Color( COL_TRANSPARENT ) )
        maFont.SetFillColor( Color( COL_TRANSPARENT ) );
    if ( !maFont.IsTransparent() )
        maFont.SetTransparent( TRUE );

}
void OutputDevice::SetTextFillColor( const Color& rColor )
{
    BOOL bTransFill = rColor.GetTransparency() ? TRUE : FALSE;
    if ( maFont.GetFillColor() != rColor )
        maFont.SetFillColor( rColor );
    if ( maFont.IsTransparent() != bTransFill )
        maFont.SetTransparent( bTransFill );
}
Color OutputDevice::GetTextFillColor() const
{
    if ( maFont.IsTransparent() )
        return Color( COL_TRANSPARENT );
    else
        return maFont.GetFillColor();
}
void OutputDevice::SetTextLineColor()
{
    maTextLineColor = Color( COL_TRANSPARENT );
}
void OutputDevice::SetTextLineColor( const Color& rColor )
{
    maTextLineColor = rColor;
}
void OutputDevice::SetOverlineColor()
{
}
void OutputDevice::SetOverlineColor( const Color& rColor )
{
}
void OutputDevice::SetTextAlign( TextAlign eAlign )
{
    if ( maFont.GetAlign() != eAlign )
    {
        maFont.SetAlign( eAlign );
        mbNewFont = TRUE;
    }
}
// -----------------------------------------------------------------------

void OutputDevice::SetBackground()
{
	maBackground = Wallpaper();
	mbBackground = FALSE;
}

// -----------------------------------------------------------------------

void OutputDevice::SetBackground( const Wallpaper& rBackground )
{
	maBackground = rBackground;

	if( rBackground.GetStyle() == WALLPAPER_NULL )
		mbBackground = FALSE;
	else
		mbBackground = TRUE;
}
USHORT OutputDevice::GetBitCount() const
{
#ifdef AVS
	if ( meOutDevType == OUTDEV_VIRDEV )
#endif
		return ((OutputDevice*)this)->mnBitCount;
#ifdef AVS
	// we need a graphics
	if ( !mpGraphics )
		return 0;

	return (USHORT)mpGraphics->GetBitCount();
#endif
}
void OutputDevice::SetDigitLanguage( LanguageType eLang )
{
    meTextLanguage = eLang;
}
void OutputDevice::SetLayoutMode( ULONG nTextLayoutMode )
{
    mnTextLayoutMode = nTextLayoutMode;
}
void OutputDevice::SetRefPoint()
{
	mbRefPoint = FALSE;
	maRefPoint.X() = maRefPoint.Y() = 0L;
}

// -----------------------------------------------------------------------

void OutputDevice::SetRefPoint( const Point& rRefPoint )
{
	mbRefPoint = TRUE;
	maRefPoint = rRefPoint;
}
void OutputDevice::Push( USHORT nFlags )
{
	ImplObjStack* pData = new ImplObjStack;
	pData->mpPrev = mpObjStack;
	mpObjStack = pData;

	pData->mnFlags = nFlags;

	if ( nFlags & PUSH_LINECOLOR )
	{
		if ( mbLineColor )
			pData->mpLineColor = new Color( maLineColor );
		else
			pData->mpLineColor = NULL;
	}
	if ( nFlags & PUSH_FILLCOLOR )
	{
		if ( mbFillColor )
			pData->mpFillColor = new Color( maFillColor );
		else
			pData->mpFillColor = NULL;
	}
	if ( nFlags & PUSH_FONT )
		pData->mpFont = new Font( maFont );
	if ( nFlags & PUSH_TEXTCOLOR )
		pData->mpTextColor = new Color( GetTextColor() );
	if ( nFlags & PUSH_TEXTFILLCOLOR )
	{
		if ( IsTextFillColor() )
			pData->mpTextFillColor = new Color( GetTextFillColor() );
		else
			pData->mpTextFillColor = NULL;
	}
	if ( nFlags & PUSH_TEXTLINECOLOR )
	{
		if ( IsTextLineColor() )
			pData->mpTextLineColor = new Color( GetTextLineColor() );
		else
			pData->mpTextLineColor = NULL;
	}
	if ( nFlags & PUSH_OVERLINECOLOR )
	{
		if ( IsOverlineColor() )
			pData->mpOverlineColor = new Color( GetOverlineColor() );
		else
			pData->mpOverlineColor = NULL;
	}
	if ( nFlags & PUSH_TEXTALIGN )
		pData->meTextAlign = GetTextAlign();
	if( nFlags & PUSH_TEXTLAYOUTMODE )
		pData->mnTextLayoutMode = GetLayoutMode();
	if( nFlags & PUSH_TEXTLANGUAGE )
		pData->meTextLanguage = GetDigitLanguage();
	if ( nFlags & PUSH_RASTEROP )
		pData->meRasterOp = GetRasterOp();
	if ( nFlags & PUSH_MAPMODE )
	{
		if ( mbMap )
			pData->mpMapMode = new MapMode( maMapMode );
		else
			pData->mpMapMode = NULL;
	}
	if ( nFlags & PUSH_CLIPREGION )
	{
		if ( mbClipRegion )
			pData->mpClipRegion = new Region( maRegion );
		else
			pData->mpClipRegion = NULL;
	}
	if ( nFlags & PUSH_REFPOINT )
	{
		if ( mbRefPoint )
			pData->mpRefPoint = new Point( maRefPoint );
		else
			pData->mpRefPoint = NULL;
	}
}

// -----------------------------------------------------------------------

void OutputDevice::Pop()
{
	ImplObjStack*	pData = mpObjStack;

	if ( !pData )
	{
		//DBG_ERRORFILE( "OutputDevice::Pop() without OutputDevice::Push()" );
		return;
	}

	mpObjStack = pData->mpPrev;

	if ( pData->mnFlags & PUSH_LINECOLOR )
	{
		if ( pData->mpLineColor )
			SetLineColor( *pData->mpLineColor );
		else
			SetLineColor();
	}
	if ( pData->mnFlags & PUSH_FILLCOLOR )
	{
		if ( pData->mpFillColor )
			SetFillColor( *pData->mpFillColor );
		else
			SetFillColor();
	}
	if ( pData->mnFlags & PUSH_FONT )
		SetFont( *pData->mpFont );
	if ( pData->mnFlags & PUSH_TEXTCOLOR )
		SetTextColor( *pData->mpTextColor );
	if ( pData->mnFlags & PUSH_TEXTFILLCOLOR )
	{
		if ( pData->mpTextFillColor )
			SetTextFillColor( *pData->mpTextFillColor );
		else
			SetTextFillColor();
	}
	if ( pData->mnFlags & PUSH_TEXTLINECOLOR )
	{
		if ( pData->mpTextLineColor )
			SetTextLineColor( *pData->mpTextLineColor );
		else
			SetTextLineColor();
	}
	if ( pData->mnFlags & PUSH_OVERLINECOLOR )
	{
		if ( pData->mpOverlineColor )
			SetOverlineColor( *pData->mpOverlineColor );
		else
			SetOverlineColor();
	}
	if ( pData->mnFlags & PUSH_TEXTALIGN )
		SetTextAlign( pData->meTextAlign );
	if( pData->mnFlags & PUSH_TEXTLAYOUTMODE )
		SetLayoutMode( pData->mnTextLayoutMode );
	if( pData->mnFlags & PUSH_TEXTLANGUAGE )
		SetDigitLanguage( pData->meTextLanguage );
	if ( pData->mnFlags & PUSH_RASTEROP )
		SetRasterOp( pData->meRasterOp );
	if ( pData->mnFlags & PUSH_MAPMODE )
	{
		if ( pData->mpMapMode )
			SetMapMode( *pData->mpMapMode );
		else
			SetMapMode();
	}
	if ( pData->mnFlags & PUSH_CLIPREGION )
		ImplSetClipRegion( pData->mpClipRegion );
	if ( pData->mnFlags & PUSH_REFPOINT )
	{
		if ( pData->mpRefPoint )
			SetRefPoint( *pData->mpRefPoint );
		else
			SetRefPoint();
	}

	ImplDeleteObjStack( pData );
}
void OutputDevice::SetFont( const Font& rNewFont )
{
    Font aFont( rNewFont );

    if ( !maFont.IsSameInstance( aFont ) )
    {
        // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
        // because SetTextColor() is used for this.
        // #i28759# maTextColor might have been changed behind our back, commit then, too.
        if( aFont.GetColor() != COL_TRANSPARENT
        && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
        {
            maTextColor = aFont.GetColor();
            mbInitTextColor = TRUE;
        }
        maFont      = aFont;
        mbNewFont   = TRUE;
    }
}

#ifdef QWE
// -----------------------------------------------------------------------

void OutputDevice::SetDrawMode( ULONG nDrawMode )
{
	mnDrawMode = nDrawMode;
}
#endif
void OutputDevice::SetRasterOp( RasterOp eRasterOp )
{
	if ( meRasterOp != eRasterOp )
	{
		meRasterOp = eRasterOp;
		mbInitLineColor = mbInitFillColor = TRUE;

		if( mpGraphics )
			mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
	}
}
BOOL OutputDevice::SetOutputSizePixel( const Size& rNewSize, BOOL bErase )
{
    return ImplSetOutputSizePixel(rNewSize, bErase);
}
BOOL OutputDevice::ImplSetOutputSizePixel( const Size& rNewSize, BOOL bErase )
{
#ifdef AVS
	DBG_TRACE3( "VirtualDevice::ImplSetOutputSizePixel( %ld, %ld, %d )", rNewSize.Width(), rNewSize.Height(), (int)bErase );

	if ( !mpVirDev )
		return FALSE;
	else 
#endif
		if ( rNewSize == GetOutputSizePixel() )
	{
		if ( bErase )
			Erase();
		return TRUE;
	}

	BOOL bRet;
	long nNewWidth = rNewSize.Width(), nNewHeight = rNewSize.Height();

	if ( nNewWidth < 1 )
		nNewWidth = 1;

	if ( nNewHeight < 1 )
		nNewHeight = 1;
#ifdef AVS
	if ( bErase )
	{
		bRet = mpVirDev->SetSize( nNewWidth, nNewHeight );

		if ( bRet )
		{
#endif
			mnOutWidth	= rNewSize.Width();
			mnOutHeight = rNewSize.Height();
			mpGraphics->SetOutputSizePixel( rNewSize );
			Erase();
#ifdef AVS
		}
	}
	else
	{
		SalVirtualDevice*	pNewVirDev;
		ImplSVData* 		pSVData = ImplGetSVData();

		// we need a graphics
		if ( !mpGraphics )
			return FALSE;

		pNewVirDev = pSVData->mpDefInst->CreateVirtualDevice( mpGraphics, nNewWidth, nNewHeight, mnBitCount );
		if ( pNewVirDev )
		{
			SalGraphics* pGraphics = pNewVirDev->GetGraphics();
			if ( pGraphics )
			{
				SalTwoRect aPosAry;
				long nWidth;
				long nHeight;
				if ( mnOutWidth < nNewWidth )
					nWidth = mnOutWidth;
				else
					nWidth = nNewWidth;
				if ( mnOutHeight < nNewHeight )
					nHeight = mnOutHeight;
				else
					nHeight = nNewHeight;
				aPosAry.mnSrcX		 = 0;
				aPosAry.mnSrcY		 = 0;
				aPosAry.mnSrcWidth	 = nWidth;
				aPosAry.mnSrcHeight  = nHeight;
				aPosAry.mnDestX 	 = 0;
				aPosAry.mnDestY 	 = 0;
				aPosAry.mnDestWidth  = nWidth;
				aPosAry.mnDestHeight = nHeight;

				pGraphics->CopyBits( &aPosAry, mpGraphics, this, this );
				pNewVirDev->ReleaseGraphics( pGraphics );
				ImplReleaseGraphics();
				pSVData->mpDefInst->DestroyVirtualDevice( mpVirDev );
				mpVirDev = pNewVirDev;
				mnOutWidth	= rNewSize.Width();
				mnOutHeight = rNewSize.Height();
				bRet = TRUE;
			}
			else
			{
				bRet = FALSE;
				pSVData->mpDefInst->DestroyVirtualDevice( pNewVirDev );
			}
		}
		else
			bRet = FALSE;
	}
	return bRet;
#endif
	return TRUE;
}
Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
{
	Bitmap	aBmp;
	long	nX = ImplLogicXToDevicePixel( rSrcPt.X() );
	long	nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
	long	nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
	long	nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );

	if ( mpGraphics )
	{
		if ( nWidth && nHeight )
		{
			Rectangle	aRect( Point( nX, nY ), Size( nWidth, nHeight ) );
			BOOL		bClipped = FALSE;

			// X-Koordinate ausserhalb des Bereichs?
			if ( nX < mnOutOffX )
			{
				nWidth -= ( mnOutOffX - nX );
				nX = mnOutOffX;
				bClipped = TRUE;
			}

			// Y-Koordinate ausserhalb des Bereichs?
			if ( nY < mnOutOffY )
			{
				nHeight -= ( mnOutOffY - nY );
				nY = mnOutOffY;
				bClipped = TRUE;
			}

			// Breite ausserhalb des Bereichs?
			if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) )
			{
				nWidth	= mnOutOffX + mnOutWidth - nX;
				bClipped = TRUE;
			}

			// Hoehe ausserhalb des Bereichs?
			if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) )
			{
				nHeight = mnOutOffY + mnOutHeight - nY;
				bClipped = TRUE;
			}

			if ( bClipped )
			{
				// Falls auf den sichtbaren Bereich geclipped wurde,
				// muessen wir eine Bitmap in der rchtigen Groesse
				// erzeugen, in die die geclippte Bitmap an die angepasste
				// Position kopiert wird
				OutputDevice aVDev( *this );

				if ( aVDev.SetOutputSizePixel( aRect.GetSize() ) )
				{
					if ( ((OutputDevice*)&aVDev)->mpGraphics )
					{
						SalTwoRect aPosAry;

						aPosAry.mnSrcX = nX;
						aPosAry.mnSrcY = nY;
						aPosAry.mnSrcWidth = nWidth;
						aPosAry.mnSrcHeight = nHeight;
						aPosAry.mnDestX = ( aRect.Left() < mnOutOffX ) ? ( mnOutOffX - aRect.Left() ) : 0L;
						aPosAry.mnDestY = ( aRect.Top() < mnOutOffY ) ? ( mnOutOffY - aRect.Top() ) : 0L;
						aPosAry.mnDestWidth = nWidth;
						aPosAry.mnDestHeight = nHeight;

						if ( (nWidth > 0) && (nHeight > 0) )
							(((OutputDevice*)&aVDev)->mpGraphics)->CopyBits( &aPosAry, mpGraphics, this, this );

						aBmp = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
					 }
					 else
						bClipped = FALSE;
				}
				else
					bClipped = FALSE;
			}

			if ( !bClipped )
			{
				SalBitmap* pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, this );

				if( pSalBmp )
				{
					ImpBitmap* pImpBmp = new ImpBitmap;
					pImpBmp->ImplSetSalBitmap( pSalBmp );
					aBmp.ImplSetImpBitmap( pImpBmp );
				}
			}
		}
	}

	return aBmp;
}
void OutputDevice::Erase()
{ 
	if ( mbBackground )
	{
		RasterOp eRasterOp = GetRasterOp();
		if ( eRasterOp != ROP_OVERPAINT )
			SetRasterOp( ROP_OVERPAINT );
		ImplDrawWallpaper( 0, 0, mnOutWidth, mnOutHeight, maBackground );
		if ( eRasterOp != ROP_OVERPAINT )
			SetRasterOp( eRasterOp );
	}
}


BOOL OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr,
    int nIndex, int nLen, int nBase, MetricVector& rVector )
{

    rVector.clear();

    if( nLen == STRING_LEN )
        nLen = rStr.Len() - nIndex;

    Rectangle aRect;
    for( int i = 0; i < nLen; i++ )
    {
        if( !GetTextBoundRect( aRect, rStr, static_cast<xub_StrLen>(nBase), static_cast<xub_StrLen>(nIndex+i), 1 ) )
            break;
        aRect.Move( rOrigin.X(), rOrigin.Y() );
        rVector.push_back( aRect );
    }

    return (nLen == (int)rVector.size());
}
BOOL OutputDevice::GetTextBoundRect( Rectangle& rRect,
    const String& rStr, xub_StrLen nBase, xub_StrLen nIndex,
    xub_StrLen nLen ) const
{
    //DBG_TRACE( "OutputDevice::GetTextBoundRect()" );
    //DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    BOOL bRet = FALSE;
    rRect.SetEmpty();

    SalLayout* pSalLayout = NULL;
    // calculate offset when nBase!=nIndex
    long nXOffset = 0;
    if( nBase != nIndex )
    {
        xub_StrLen nStart = Min( nBase, nIndex );
        xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
        pSalLayout = ImplLayout( rStr, nStart, nOfsLen );
        if( pSalLayout )
        {
            nXOffset = pSalLayout->GetTextWidth();
            nXOffset /= pSalLayout->GetUnitsPerPixel();
            pSalLayout->Release();
            // TODO: fix offset calculation for Bidi case
            if( nBase < nIndex)
                nXOffset = -nXOffset;
        }
    }

    pSalLayout = ImplLayout( rStr, nIndex, nLen );
    Rectangle aPixelRect;
    if( pSalLayout )
    {
        bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );

        if( bRet )
        {
            int nWidthFactor = pSalLayout->GetUnitsPerPixel();

            if( nWidthFactor > 1 )
            {
                double fFactor = 1.0 / nWidthFactor;
                aPixelRect.Left()
                    = static_cast< long >(aPixelRect.Left() * fFactor);
                aPixelRect.Right()
                    = static_cast< long >(aPixelRect.Right() * fFactor);
                aPixelRect.Top()
                    = static_cast< long >(aPixelRect.Top() * fFactor);
                aPixelRect.Bottom()
                    = static_cast< long >(aPixelRect.Bottom() * fFactor);
            }

            Point aRotatedOfs( mnTextOffX, mnTextOffY );
            aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
            aPixelRect += aRotatedOfs;
            rRect = PixelToLogic( aPixelRect );
            if( mbMap )
                rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
        }

        pSalLayout->Release();
    }
	return TRUE;
#ifdef AVS

    if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
        return bRet;

    // fall back to bitmap method to get the bounding rectangle,
    // so we need a monochrome virtual device with matching font
    VirtualDevice aVDev( 1 );
    Font aFont( GetFont() );
    aFont.SetShadow( FALSE );
    aFont.SetOutline( FALSE );
    aFont.SetRelief( RELIEF_NONE );
    aFont.SetOrientation( 0 );
    aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
    aVDev.SetFont( aFont );
    aVDev.SetTextAlign( ALIGN_TOP );

    // layout the text on the virtual device
    pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen );
    if( !pSalLayout )
        return false;

    // make the bitmap big enough
    // TODO: use factors when it would get too big
    long nWidth = pSalLayout->GetTextWidth();
    long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
    Point aOffset( nWidth/2, 8 );
    Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
    if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
        return false;

    // draw text in black
    pSalLayout->DrawBase() = aOffset;
    aVDev.SetTextColor( Color( COL_BLACK ) );
    aVDev.SetTextFillColor();
    aVDev.ImplInitTextColor();
    aVDev.ImplDrawText( *pSalLayout );
    pSalLayout->Release();

    // find extents using the bitmap
    Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
    BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
    if( !pAcc )
        return FALSE;
    const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
    const long nW = pAcc->Width();
    const long nH = pAcc->Height();
    long nLeft = 0;
    long nRight = 0;

    // find top left point
    long nTop = 0;
    for(; nTop < nH; ++nTop )
    {
        for( nLeft = 0; nLeft < nW; ++nLeft )
            if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
                break;
        if( nLeft < nW )
            break;
    }

    // find bottom right point
    long nBottom = nH;
    while( --nBottom >= nTop )
    {
        for( nRight = nW; --nRight >= 0; )
            if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
                break;
        if( nRight >= 0 )
            break;
    }
    if( nRight < nLeft )
    {
        long nX = nRight;
        nRight = nLeft;
        nLeft  = nX;
    }

    for( long nY = nTop; nY <= nBottom; ++nY )
    {
        // find leftmost point
        long nX;
        for( nX = 0; nX < nLeft; ++nX )
            if( pAcc->GetPixel( nY, nX ) == aBlack )
                break;
        nLeft = nX;

        // find rightmost point
        for( nX = nW; --nX > nRight; )
            if( pAcc->GetPixel( nY, nX ) == aBlack )
                break;
        nRight = nX;
    }

    aBmp.ReleaseAccess( pAcc );

    if( nTop <= nBottom )
    {
        Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
        Point aTopLeft( nLeft, nTop );
        aTopLeft -= aOffset;
        // adjust to text alignment
        aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
        // convert to logical coordinates
        aSize = PixelToLogic( aSize );
        aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
        aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
        rRect = Rectangle( aTopLeft, aSize );
        return TRUE;
    }

    return FALSE;
#endif
}

// =======================================================================

ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData )
:   maFontSelData( rFontSelData ),
    maMetric( rFontSelData ),
    mpConversion( NULL ),
    mnRefCount( 1 ),
    mnSetFontFlags( 0 ),
    mnOwnOrientation( 0 ),
    mnOrientation( 0 ),
    mbInit( false )//,
    //mpUnicodeFallbackList( NULL )
{
    maFontSelData.mpFontEntry = this;
}

// -----------------------------------------------------------------------

ImplFontEntry::~ImplFontEntry()
{
    //delete mpUnicodeFallbackList;
}
// -----------------------------------------------------------------------

static String GetNextFontToken( const String& rTokenStr, xub_StrLen& rIndex )
{
    // check for valid start index
    int nStringLen = rTokenStr.Len();
    if( rIndex >= nStringLen )
    {
        rIndex = STRING_NOTFOUND;
        return String();
    }

    // find the next token delimiter and return the token substring
    const sal_Unicode* pStr = rTokenStr.GetBuffer() + rIndex;
    const sal_Unicode* pEnd = rTokenStr.GetBuffer() + nStringLen;
    for(; pStr < pEnd; ++pStr )
        if( (*pStr == ';') || (*pStr == ',') )
            break;

    xub_StrLen nTokenStart = rIndex;
    xub_StrLen nTokenLen;
    if( pStr < pEnd )
    {
        rIndex = static_cast<xub_StrLen>(pStr - rTokenStr.GetBuffer());
        nTokenLen = rIndex - nTokenStart;
        ++rIndex; // skip over token separator
    }
    else
    {
        // no token delimiter found => handle last token
        rIndex = STRING_NOTFOUND;
        nTokenLen = STRING_LEN;

        // optimize if the token string consists of just one token
        if( !nTokenStart )
            return rTokenStr;
    }

    return String( rTokenStr, nTokenStart, nTokenLen );
}

ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData )
:   ImplFontAttributes( rFontSelData )
{

	// initialize the members provided by the font request
    mnWidth        = rFontSelData.mnWidth;
    mnOrientation  = static_cast<short>(rFontSelData.mnOrientation);

	// intialize the used font name 
    if( rFontSelData.mpFontData )
    {
        maName     = rFontSelData.mpFontData->maName;
        maStyleName= rFontSelData.mpFontData->maStyleName;
        mbDevice   = rFontSelData.mpFontData->mbDevice;
        mbKernableFont = true;
    }
    else
    {
        xub_StrLen nTokenPos = 0;
        maName     = GetNextFontToken( rFontSelData.maName, nTokenPos );
        maStyleName= rFontSelData.maStyleName;
        mbDevice   = false;
        mbKernableFont = false;
    }

	// reset metrics that are usually measured for the font instance 
    mnAscent       = 0;
    mnDescent      = 0;
    mnIntLeading   = 0;
    mnExtLeading   = 0;
    mnSlant        = 0;
    mnMinKashida   = 0;

    // reset metrics that are usually derived from the measurements
    mnUnderlineSize            = 0;
    mnUnderlineOffset          = 0;
    mnBUnderlineSize           = 0;
    mnBUnderlineOffset         = 0;
    mnDUnderlineSize           = 0;
    mnDUnderlineOffset1        = 0;
    mnDUnderlineOffset2        = 0;
    mnWUnderlineSize           = 0;
    mnWUnderlineOffset         = 0;
    mnAboveUnderlineSize       = 0;
    mnAboveUnderlineOffset     = 0;
    mnAboveBUnderlineSize      = 0;
    mnAboveBUnderlineOffset    = 0;
    mnAboveDUnderlineSize      = 0;
    mnAboveDUnderlineOffset1   = 0;
    mnAboveDUnderlineOffset2   = 0;
    mnAboveWUnderlineSize      = 0;
    mnAboveWUnderlineOffset    = 0;
    mnStrikeoutSize            = 0;
    mnStrikeoutOffset          = 0;
    mnBStrikeoutSize           = 0;
    mnBStrikeoutOffset         = 0;
    mnDStrikeoutSize           = 0;
    mnDStrikeoutOffset1        = 0;
    mnDStrikeoutOffset2        = 0;

}
// -----------------------------------------------------------------------

void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
{
    long nDescent = mnDescent;
    if ( nDescent <= 0 )
    {
        nDescent = mnAscent / 10;
        if ( !nDescent )
            nDescent = 1;
    }

    // #i55341# for some fonts it is not a good idea to calculate
    // their text line metrics from the real font descent
    // => work around this problem just for these fonts
    if( 3*nDescent > mnAscent )
        nDescent = mnAscent / 3;

    long nLineHeight = ((nDescent*25)+50) / 100;
    if ( !nLineHeight )
        nLineHeight = 1;
    long nLineHeight2 = nLineHeight / 2;
    if ( !nLineHeight2 )
        nLineHeight2 = 1;

    long nBLineHeight = ((nDescent*50)+50) / 100;
    if ( nBLineHeight == nLineHeight )
        nBLineHeight++;
    long nBLineHeight2 = nBLineHeight/2;
    if ( !nBLineHeight2 )
        nBLineHeight2 = 1;

    long n2LineHeight = ((nDescent*16)+50) / 100;
    if ( !n2LineHeight )
        n2LineHeight = 1;
    long n2LineDY = n2LineHeight;
     /* #117909#
      * add some pixels to minimum double line distance on higher resolution devices
      */
    long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150;
    if ( n2LineDY < nMin2LineDY )
        n2LineDY = nMin2LineDY;
    long n2LineDY2 = n2LineDY/2;
    if ( !n2LineDY2 )
        n2LineDY2 = 1;

    long nUnderlineOffset = mnDescent/2 + 1;
    long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);

    mnUnderlineSize        = nLineHeight;
    mnUnderlineOffset      = nUnderlineOffset - nLineHeight2;

    mnBUnderlineSize       = nBLineHeight;
    mnBUnderlineOffset     = nUnderlineOffset - nBLineHeight2;

    mnDUnderlineSize       = n2LineHeight;
    mnDUnderlineOffset1    = nUnderlineOffset - n2LineDY2 - n2LineHeight;
    mnDUnderlineOffset2    = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;

    long nWCalcSize = mnDescent;
    if ( nWCalcSize < 6 )
    {
        if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
            mnWUnderlineSize = nWCalcSize;
        else
            mnWUnderlineSize = 3;
    }
    else
        mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;

    // #109280# the following line assures that wavelnes are never placed below the descent, however
    // for most fonts the waveline then is drawn into the text, so we better keep the old solution
    // pFontEntry->maMetric.mnWUnderlineOffset     = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize;
    mnWUnderlineOffset     = nUnderlineOffset;

    mnStrikeoutSize        = nLineHeight;
    mnStrikeoutOffset      = nStrikeoutOffset - nLineHeight2;

    mnBStrikeoutSize       = nBLineHeight;
    mnBStrikeoutOffset     = nStrikeoutOffset - nBLineHeight2;

    mnDStrikeoutSize       = n2LineHeight;
    mnDStrikeoutOffset1    = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
    mnDStrikeoutOffset2    = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
}

// -----------------------------------------------------------------------

void ImplFontMetricData::ImplInitAboveTextLineSize()
{
    long nIntLeading = mnIntLeading;
    // TODO: assess usage of nLeading below (changed in extleading CWS)
    // if no leading is available, we assume 15% of the ascent
    if ( nIntLeading <= 0 )
    {
        nIntLeading = mnAscent*15/100;
        if ( !nIntLeading )
            nIntLeading = 1;
    }

    long nLineHeight = ((nIntLeading*25)+50) / 100;
    if ( !nLineHeight )
        nLineHeight = 1;

    long nBLineHeight = ((nIntLeading*50)+50) / 100;
    if ( nBLineHeight == nLineHeight )
        nBLineHeight++;

    long n2LineHeight = ((nIntLeading*16)+50) / 100;
    if ( !n2LineHeight )
        n2LineHeight = 1;

    long nCeiling = -mnAscent;

    mnAboveUnderlineSize       = nLineHeight;
    mnAboveUnderlineOffset     = nCeiling + (nIntLeading - nLineHeight + 1) / 2;

    mnAboveBUnderlineSize      = nBLineHeight;
    mnAboveBUnderlineOffset    = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;

    mnAboveDUnderlineSize      = n2LineHeight;
    mnAboveDUnderlineOffset1   = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
    mnAboveDUnderlineOffset2   = nCeiling + (nIntLeading +   n2LineHeight + 1) / 2;

    long nWCalcSize = nIntLeading;
    if ( nWCalcSize < 6 )
    {
        if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
            mnAboveWUnderlineSize = nWCalcSize;
        else
            mnAboveWUnderlineSize = 3;
    }
    else
        mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;

    mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
}
// =======================================================================

ImplDevFontList::ImplDevFontList()
:   mbMatchData( false )
,   mbMapNames( false )
,   mpPreMatchHook( NULL )
,   mpFallbackHook( NULL )
,   mpFallbackList( NULL )
,   mnFallbackCount( -1 )
{}


// -----------------------------------------------------------------------

ImplDevFontList::~ImplDevFontList()
{
    Clear();
}
// -----------------------------------------------------------------------

void ImplDevFontList::Clear()
{
    // remove fallback lists
    delete[] mpFallbackList;
    mpFallbackList = NULL;
    mnFallbackCount = -1;

    // clear all entries in the device font list
    DevFontList::iterator it = maDevFontList.begin();
    for(; it != maDevFontList.end(); ++it )
    {
        ImplDevFontListData* pEntry = (*it).second;
        delete pEntry;
    }

    maDevFontList.clear();

    // match data must be recalculated too
    mbMatchData = false;
}

// -----------------------------------------------------------------------

void ImplDevFontList::Add( ImplFontData* pNewData )
{
    int nAliasQuality = pNewData->mnQuality - 100;
    String aMapNames = pNewData->maMapNames;
    pNewData->maMapNames = String();

    bool bKeepNewData = false;
    for( xub_StrLen nMapNameIndex = 0; nMapNameIndex != STRING_NOTFOUND; )
    {
        String aSearchName = pNewData->maName;
#ifdef AVS
        ImplGetEnglishSearchFontName( aSearchName );
#endif
        DevFontList::const_iterator it = maDevFontList.find( aSearchName );
        ImplDevFontListData* pFoundData = NULL;
        if( it != maDevFontList.end() )
            pFoundData = (*it).second;

        if( !pFoundData )
        {
            pFoundData = new ImplDevFontListData( aSearchName );
            maDevFontList[ aSearchName ] = pFoundData;
        }

        bKeepNewData = pFoundData->AddFontFace( pNewData );

        // add font alias if available
        // a font alias should never win against an original font with similar quality
        if( aMapNames.Len() >= nMapNameIndex )
            break;
        if( bKeepNewData ) // try to recycle obsoleted object
            pNewData = pNewData->CreateAlias();
        bKeepNewData = false;
        pNewData->mnQuality = nAliasQuality;
        pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex );
    }

    if( !bKeepNewData )
        delete pNewData;
}
namespace SVMCore{
// -----------------------------------------------------------------------

void ImplGetEnglishSearchFontName( String& rName )
{
    bool        bNeedTranslation = false;
    xub_StrLen  nLen = rName.Len();

    // Remove trailing whitespaces
    xub_StrLen i = nLen;
    while ( i && (rName.GetChar( i-1 ) < 32) )
        i--;
    if ( i != nLen )
        rName.Erase( i );

    // Remove Script at the end
    // Scriptname must be the last part of the fontname and
    // looks like "fontname (scriptname)". So there can only be a
    // script name at the and of the fontname, when the last char is ')'
    if ( (nLen >= 3) && rName.GetChar( nLen-1 ) == ')' )
    {
        int nOpen = 1;
        xub_StrLen nTempLen = nLen-2;
        while ( nTempLen )
        {
            if ( rName.GetChar( nTempLen ) == '(' )
            {
                nOpen--;
                if ( !nOpen )
                {
                    // Remove Space at the end
                    if ( nTempLen && (rName.GetChar( nTempLen-1 ) == ' ') )
                        nTempLen--;
                    rName.Erase( nTempLen );
                    nLen = nTempLen;
                    break;
                }
            }
            if ( rName.GetChar( nTempLen ) == ')' )
                nOpen++;
            nTempLen--;
        }
    }

    // remove all whitespaces and converts to lower case ASCII
    // TODO: better transliteration to ASCII e.g. all digits
    i = 0;
    while ( i < nLen )
    {
        sal_Unicode c = rName.GetChar( i );
        if ( c > 127 )
        {
            // Translate to Lowercase-ASCII
            // FullWidth-ASCII to half ASCII
            if ( (c >= 0xFF00) && (c <= 0xFF5E) )
            {
                c -= 0xFF00-0x0020;
                // Upper to Lower
                if ( (c >= 'A') && (c <= 'Z') )
                    c += 'a' - 'A';
                rName.SetChar( i, c );
            }
            else
            {
                // Only Fontnames with None-Ascii-Characters must be translated
                bNeedTranslation = true;
            }
        }
        // not lowercase Ascii
        else if ( !((c >= 'a') && (c <= 'z')) )
        {
            // To Lowercase-Ascii
            if ( (c >= 'A') && (c <= 'Z') )
            {
                c += 'a' - 'A';
                rName.SetChar( i, c );
            }
            else if( ((c < '0') || (c > '9')) && (c != ';') ) // not 0-9 or semicolon
            {
                // Remove white spaces and special characters
                rName.Erase( i, 1 );
                nLen--;
                continue;
            }
        }

        i++;
    }
#ifdef AVS
    // translate normalized localized name to its normalized English ASCII name
    if( bNeedTranslation )
    {
        typedef std::hash_map<const String, const char*,FontNameHash> FontNameDictionary;
        static FontNameDictionary aDictionary( sizeof(aImplLocalizedNamesList) / sizeof(*aImplLocalizedNamesList) );

        // the font name dictionary needs to be intialized once
        if( aDictionary.empty() )
        {
            // TODO: check if all dictionary entries are already normalized?
            const ImplLocalizedFontName* pList = aImplLocalizedNamesList;
            for(; pList->mpEnglishName; ++pList )
                aDictionary[ pList->mpLocalizedNames ] = pList->mpEnglishName;
        }

        FontNameDictionary::const_iterator it = aDictionary.find( rName );
        if( it != aDictionary.end() )
            rName.AssignAscii( it->second );
    }
#endif
}
}//SVMCore

// -----------------------------------------------------------------------

ImplFontCache::ImplFontCache( bool bPrinter )
:   mpFirstEntry( NULL ),
    mnRef0Count( 0 ),
    mbPrinter( bPrinter )
{}

// -----------------------------------------------------------------------

ImplFontCache::~ImplFontCache()
{
#ifdef AVS
    FontInstanceList::iterator it = maFontInstanceList.begin();
    for(; it != maFontInstanceList.end(); ++it )
    {
        ImplFontEntry* pEntry = (*it).second;
        delete pEntry;
    }
#endif
}
// -----------------------------------------------------------------------

void ImplFontCache::Release( ImplFontEntry* pEntry )
{
    static const int FONTCACHE_MAX = 50;

   // DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" );
    if( --pEntry->mnRefCount > 0 )
        return;

    if( ++mnRef0Count < FONTCACHE_MAX )
        return;
#ifdef AVS
    // remove unused entries from font instance cache
    FontInstanceList::iterator it_next = maFontInstanceList.begin();
    while( it_next != maFontInstanceList.end() )
    {
        FontInstanceList::iterator it = it_next++;
        ImplFontEntry* pFontEntry = (*it).second;
        if( pFontEntry->mnRefCount > 0 )
            continue;

        maFontInstanceList.erase( it );
        delete pFontEntry;
        --mnRef0Count;
        DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" );

        if( mpFirstEntry == pFontEntry )
            mpFirstEntry = NULL;
    }
#endif
    //DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" );
}
// -----------------------------------------------------------------------

ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
    const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific )
{
    String aSearchName = rFont.GetName();
#ifdef QWE
    // TODO: also add device specific name caching
    if( !pDevSpecific )
    {
        // check if the requested font name is already known
        // if it is already known get its normalized search name
        FontNameList::const_iterator it_name = maFontNameList.find( aSearchName );
        if( it_name != maFontNameList.end() )
            if( !(*it_name).second.EqualsAscii( "hg", 0, 2) )
                aSearchName = (*it_name).second;
    }
#endif
    // initialize internal font request object
    ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight );
    return GetFontEntry( pFontList, aFontSelData, pDevSpecific );
}

// -----------------------------------------------------------------------

ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
    ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific )
{
    // check if a directly matching logical font instance is already cached,
    // the most recently used font usually has a hit rate of >50%
    ImplFontEntry *pEntry = NULL;
    ImplDevFontListData* pFontFamily = NULL;
#ifdef  AVS
    IFSD_Equal aIFSD_Equal;
    if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
        pEntry = mpFirstEntry;
    else
    {
        FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
        if( it != maFontInstanceList.end() )
            pEntry = (*it).second;
    }

    if( !pEntry ) // no direct cache hit
    {
#endif
		int nCount = pFontList->Count();
        // find the best matching logical font family and update font selector accordingly
        pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific );
        //DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" );
        if( pFontFamily )
            aFontSelData.maSearchName = pFontFamily->GetSearchName();
#ifdef AVS
        // check if an indirectly matching logical font instance is already cached
        FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
        if( it != maFontInstanceList.end() )
        {
            // we have an indirect cache hit
            pEntry = (*it).second;
            // cache the requested and the selected font names
            // => next time there is a good chance for a direct cache hit
            // don't allow the cache to grow too big
            // TODO: implement some fancy LRU caching?
            if( maFontNameList.size() >= 4000 )
                maFontNameList.clear();
            // TODO: also add device specific name caching
            if( !pDevSpecific )
                if( aFontSelData.maName != aFontSelData.maSearchName )
                    maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName;
        }
    }
    if( pEntry ) // cache hit => use existing font instance
    {
        // increase the font instance's reference count
        if( !pEntry->mnRefCount++ )
            --mnRef0Count;
    }

    else // no cache hit => create a new font instance
    {
#endif
        // find the best matching physical font face
        ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData );
        aFontSelData.mpFontData = pFontData;

        // create a new logical font instance from this physical font face
        pEntry = pFontData->CreateFontInstance( aFontSelData );
#ifdef QWE
        // if we found a different symbol font we need a symbol conversion table
        if( pFontData->IsSymbolFont() )
            if( aFontSelData.maTargetName != aFontSelData.maSearchName )
                pEntry->mpConversion = ImplGetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
        // add the new entry to the cache
        maFontInstanceList[ aFontSelData ] = pEntry;
    }
#endif
    mpFirstEntry = pEntry;
    return pEntry;
}
struct FontMatchStatus
{
public:
    int                 mnFaceMatch;
    int                 mnHeightMatch;
    int                 mnWidthMatch;
    const xub_Unicode*  mpTargetStyleName;
};
// -----------------------------------------------------------------------

ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD,
    bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const
{
    // give up if no fonts are available
    if( !Count() )
        return NULL;
    // test if a font in the token list is available
    // substitute the font if this was requested
    USHORT nSubstFlags = FONT_SUBSTITUTE_ALWAYS;
    if ( bPrinter )
        nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY;

    bool bMultiToken = false;
    xub_StrLen nTokenPos = 0;
    String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
    for(;;)
    {
        rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
        aSearchName = rFSD.maTargetName;
#ifdef AVS
        ImplGetEnglishSearchFontName( aSearchName );
        ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
        // #114999# special emboldening for Ricoh fonts
        // TODO: smarter check for special cases by using PreMatch infrastructure?
        if( (rFSD.meWeight > WEIGHT_MEDIUM)
        &&  aSearchName.EqualsAscii( "hg", 0, 2) )
        {
            String aBoldName;
            if( aSearchName.EqualsAscii( "hggothicb", 0, 9) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice"));
            else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice"));
            else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob"));
            else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob"));
            else if( aSearchName.EqualsAscii( "hgminchob" ) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe"));
            else if( aSearchName.EqualsAscii( "hgpminchob" ) )
                aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe"));

            if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) )
            {
                // the other font is available => use it
                aSearchName = aBoldName;
                // prevent synthetic emboldening of bold version
                rFSD.meWeight = WEIGHT_DONTKNOW;
            }
        }
#endif
        // check if the current font name token or its substitute is valid
        ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
        if( pFoundData )
            return pFoundData;
#ifdef AVS
        // some systems provide special customization
        // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
        //      because the system wants to map it to another font first, e.g. "Helvetica"
        if( mpPreMatchHook )
        {
            if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
            {
                ImplGetEnglishSearchFontName( aSearchName );
                pFoundData = ImplFindBySearchName( aSearchName );
                if( pFoundData )
                    return pFoundData;
            }
        }
#endif
        // break after last font name token was checked unsuccessfully
        if( nTokenPos == STRING_NOTFOUND)
            break;
        bMultiToken = true;
    }
#ifdef AVS
    // if the first font was not available find the next available font in
    // the semicolon separated list of font names. A font is also considered
    // available when there is a matching entry in the Tools->Options->Fonts
    // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution
    // font is available
    for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
    {
        if( bMultiToken )
        {
            rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
            aSearchName = rFSD.maTargetName;
            ImplGetEnglishSearchFontName( aSearchName );
        }
        else
            nTokenPos = STRING_NOTFOUND;
        if( mpPreMatchHook )
            if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
                ImplGetEnglishSearchFontName( aSearchName );
        ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
        ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
        if( pFoundData )
            return pFoundData;
    }

    // if no font with a directly matching name is available use the
    // first font name token and get its attributes to find a replacement
    if ( bMultiToken )
    {
        nTokenPos = 0;
        rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
        aSearchName = rFSD.maTargetName;
        ImplGetEnglishSearchFontName( aSearchName );
    }

    String      aSearchShortName;
    String      aSearchFamilyName;
    FontWeight  eSearchWeight   = rFSD.meWeight;
    FontWidth   eSearchWidth    = rFSD.meWidthType;
    ULONG       nSearchType     = 0;
    FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName,
                                        eSearchWeight, eSearchWidth, nSearchType );

    // note: the search name was already translated to english (if possible)

    // use the font's shortened name if needed
    if ( aSearchShortName != aSearchName )
    {
       ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName );
       if( pFoundData )
       {
#ifdef UNX
            /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is
            a korean bitmap font that is not suitable here. Use the font replacement table,
            that automatically leads to the desired "HG Mincho Light J". Same story for
            MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
            static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") );
            static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") );
            if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic))
                // TODO: add heuristic to only throw out the fake ms* fonts
#endif
            {
                return pFoundData;
            }
        }
    }

    // use font fallback
    const FontNameAttr* pFontAttr = NULL;
    if( aSearchName.Len() )
    {
        // get fallback info using FontSubstConfiguration and
        // the target name, it's shortened name and family name in that order
        const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
        pFontAttr = rFontSubst.getSubstInfo( aSearchName );
        if ( !pFontAttr && (aSearchShortName != aSearchName) )
            pFontAttr = rFontSubst.getSubstInfo( aSearchShortName );
        if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) )
            pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName );

        // try the font substitutions suggested by the fallback info
        if( pFontAttr )
        {
            ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr );
            if( pFoundData )
                return pFoundData;
        }
    }

    // if a target symbol font is not available use a default symbol font
    if( rFSD.IsSymbolFont() )
    {
        com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
        aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL );
        ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName );
        if( pFoundData )
            return pFoundData;
    }

    // now try the other font name tokens
    while( nTokenPos != STRING_NOTFOUND )
    {
        rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
        if( !rFSD.maTargetName.Len() )
            continue;

        aSearchName = rFSD.maTargetName;
        ImplGetEnglishSearchFontName( aSearchName );

        String      aTempShortName;
        String      aTempFamilyName;
        ULONG       nTempType   = 0;
        FontWeight  eTempWeight = rFSD.meWeight;
        FontWidth   eTempWidth  = WIDTH_DONTKNOW;
        FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
                                            eTempWeight, eTempWidth, nTempType );

        // use a shortend token name if available
        if( aTempShortName != aSearchName )
        {
            ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName );
            if( pFoundData )
                return pFoundData;
        }

        // use a font name from font fallback list to determine font attributes

        // get fallback info using FontSubstConfiguration and
        // the target name, it's shortened name and family name in that order
        const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
        const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName );
        if ( !pTempFontAttr && (aTempShortName != aSearchName) )
            pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
        if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
            pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );

        // try the font substitutions suggested by the fallback info
        if( pTempFontAttr )
        {
            ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr );
            if( pFoundData )
                return pFoundData;
            if( !pFontAttr )
                pFontAttr = pTempFontAttr;
        }
    }

    // if still needed use the alias names of the installed fonts
    if( mbMapNames )
    {
        ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName );
        if( pFoundData )
            return pFoundData;
    }

    // if still needed use the font request's attributes to find a good match
    switch( rFSD.meLanguage )
    {
        case LANGUAGE_CHINESE:
        case LANGUAGE_CHINESE_SIMPLIFIED:
        case LANGUAGE_CHINESE_SINGAPORE:
            nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC;
            break;
        case LANGUAGE_CHINESE_TRADITIONAL:
        case LANGUAGE_CHINESE_HONGKONG:
        case LANGUAGE_CHINESE_MACAU:
            nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC;
            break;
        case LANGUAGE_KOREAN:
        case LANGUAGE_KOREAN_JOHAB:
            nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR;
            break;
        case LANGUAGE_JAPANESE:
            nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP;
            break;
        default:
            nSearchType |= ImplIsCJKFont( rFSD.maName );
            if( rFSD.IsSymbolFont() )
                nSearchType |= IMPL_FONT_ATTR_SYMBOL;
            break;
    }

    ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr );
    ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType,
        eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName );

    if( pFoundData )
    {
        // overwrite font selection attributes using info from the typeface flags
        if( (eSearchWeight >= WEIGHT_BOLD)
        &&  (eSearchWeight > rFSD.meWeight)
        &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) )
            rFSD.meWeight = eSearchWeight;
        else if( (eSearchWeight < WEIGHT_NORMAL)
        &&  (eSearchWeight < rFSD.meWeight)
        &&  (eSearchWeight != WEIGHT_DONTKNOW)
        &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) )
            rFSD.meWeight = eSearchWeight;

        if( (nSearchType & IMPL_FONT_ATTR_ITALIC)
        &&  ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE))
        &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) )
            rFSD.meItalic = ITALIC_NORMAL;
    }
    else
    {
        // if still needed fall back to default fonts
        pFoundData = FindDefaultFont();
    }
    return pFoundData;
#endif
    return FindDefaultFont();
}
// -----------------------------------------------------------------------

ImplDevFontListData* ImplDevFontList::FindDefaultFont() const
{
#ifdef AVS
    // try to find one of the default fonts of the
    // UNICODE, SANSSERIF, SERIF or FIXED default font lists
    const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
    com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
    String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE );
    ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname );
    if( pFoundData )
        return pFoundData;

    aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS );
    pFoundData = ImplFindByTokenNames( aFontname );
    if( pFoundData )
        return pFoundData;

    aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF );
    pFoundData = ImplFindByTokenNames( aFontname );
    if( pFoundData )
        return pFoundData;

    aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED );
    pFoundData = ImplFindByTokenNames( aFontname );
    if( pFoundData )
        return pFoundData;

    // now try to find a reasonable non-symbol font

    InitMatchData();

    DevFontList::const_iterator it = maDevFontList.begin();
    for(; it !=  maDevFontList.end(); ++it )
    {
        ImplDevFontListData* pData = (*it).second;
        if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL )
            continue;
        pFoundData = pData;
        if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) )
            break;
    }
    if( pFoundData )
        return pFoundData;
#endif
	ImplDevFontListData* pFoundData = NULL;
	DevFontList::const_iterator it = maDevFontList.find( String(L"Arial") );
    if( it !=  maDevFontList.end() )
        pFoundData = (*it).second;

	if( NULL == pFoundData )
	{
		DevFontList::const_iterator it = maDevFontList.begin();
		// finding any font is better than finding no font at all
		it = maDevFontList.begin();
		if( it !=  maDevFontList.end() )
			pFoundData = (*it).second;
	}

    return pFoundData;
}
// find the font from the normalized font family name
ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const
{
#ifdef AVS
#ifdef DEBUG
    String aTempName = rSearchName;
    ImplGetEnglishSearchFontName( aTempName );
    DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" );
#endif
#endif

    DevFontList::const_iterator it = maDevFontList.find( rSearchName );
    if( it == maDevFontList.end() )
        return NULL;

    ImplDevFontListData* pFoundData = (*it).second;
    return pFoundData;

	return NULL;
}
// =======================================================================

ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic )
:   ImplDevFontAttributes( rDFA ),
    mnWidth(0),
    mnHeight(0),
    mnMagic( nMagic ),
    mpNext( NULL )
{
    // StarSymbol is a unicode font, but it still deserves the symbol flag
    if( !mbSymbolFlag )
        if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10)
        ||  0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) )
            mbSymbolFlag = true;
}
bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const
{
    int nMatch = 0;

    const String& rFontName = rFSD.maTargetName;
    if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) )
        nMatch += 240000;

    if( rStatus.mpTargetStyleName
    &&  maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) )
        nMatch += 120000;

    if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) )
        nMatch += 20000;

    // prefer NORMAL font width
    // TODO: change when the upper layers can tell their width preference
    if( meWidthType == WIDTH_NORMAL )
        nMatch += 400;
    else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) )
        nMatch += 300;

    if( rFSD.meWeight != WEIGHT_DONTKNOW )
    {
        // if not bold prefer light fonts to bold fonts
        int nReqWeight = (int)rFSD.meWeight;
        if ( rFSD.meWeight > WEIGHT_MEDIUM )
            nReqWeight += 100;

        int nGivenWeight = (int)meWeight;
        if( meWeight > WEIGHT_MEDIUM )
            nGivenWeight += 100;

        int nWeightDiff = nReqWeight - nGivenWeight;

        if ( nWeightDiff == 0 )
            nMatch += 1000;
        else if ( nWeightDiff == +1 || nWeightDiff == -1 )
            nMatch += 700;
        else if ( nWeightDiff < +50 && nWeightDiff > -50)
            nMatch += 200;
    }
    else // requested weight == WEIGHT_DONTKNOW
    {
        // prefer NORMAL font weight
        // TODO: change when the upper layers can tell their weight preference
        if( meWeight == WEIGHT_NORMAL )
            nMatch += 450;
        else if( meWeight == WEIGHT_MEDIUM )
            nMatch += 350;
        else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) )
            nMatch += 200;
        else if( meWeight == WEIGHT_LIGHT )
            nMatch += 150;
    }

    if ( rFSD.meItalic == ITALIC_NONE )
    {
        if( meItalic == ITALIC_NONE )
            nMatch += 900;
    }
    else
    {
        if( rFSD.meItalic == meItalic )
            nMatch += 900;
        else if( meItalic != ITALIC_NONE )
            nMatch += 600;
    }

    if( mbDevice )
        nMatch += 1;

    int nHeightMatch = 0;
    int nWidthMatch = 0;

    if( IsScalable() )
    {
        if( rFSD.mnOrientation != 0 )
            nMatch += 80;
        else if( rFSD.mnWidth != 0 )
            nMatch += 25;
        else
            nMatch += 5;
    }
    else
    {
        if( rFSD.mnHeight == mnHeight )
        {
            nMatch += 20;
            if( rFSD.mnWidth == mnWidth )
                nMatch += 10;
        }
        else
        {
            // for non-scalable fonts the size difference is very important
            // prefer the smaller font face because of clipping/overlapping issues
            int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000;
            nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff;
            if( rFSD.mnHeight )
                nHeightMatch /= rFSD.mnHeight;

            if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) )
            {
                int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100;
                nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff;
            }
        }
    }

    if( rStatus.mnFaceMatch > nMatch )
        return false;
    else if( rStatus.mnFaceMatch < nMatch )
    {
        rStatus.mnFaceMatch      = nMatch;
        rStatus.mnHeightMatch    = nHeightMatch;
        rStatus.mnWidthMatch     = nWidthMatch;
        return true;
    }

    // when two fonts are still competing prefer the
    // one with the best matching height
    if( rStatus.mnHeightMatch > nHeightMatch )
        return false;
    else if( rStatus.mnHeightMatch < nHeightMatch )
    {
        rStatus.mnHeightMatch    = nHeightMatch;
        rStatus.mnWidthMatch     = nWidthMatch;
        return true;
    }

    if( rStatus.mnWidthMatch > nWidthMatch )
        return false;

    rStatus.mnWidthMatch = nWidthMatch;
    return true;
}
// -----------------------------------------------------------------------

StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const
{
    // compare their width, weight, italic and style name
    if( meWidthType < rOther.meWidthType )
        return COMPARE_LESS;
    else if( meWidthType > rOther.meWidthType )
        return COMPARE_GREATER;

    if( meWeight < rOther.meWeight )
        return COMPARE_LESS;
    else if( meWeight > rOther.meWeight )
        return COMPARE_GREATER;

    if( meItalic < rOther.meItalic )
        return COMPARE_LESS;
    else if( meItalic > rOther.meItalic )
        return COMPARE_GREATER;

    StringCompare eCompare = maName.CompareTo( rOther.maName );
    return eCompare;
}

// -----------------------------------------------------------------------

StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const
{
    StringCompare eCompare = CompareIgnoreSize( rOther );
    if( eCompare != COMPARE_EQUAL )
        return eCompare;

    if( mnHeight < rOther.mnHeight )
        return COMPARE_LESS;
    else if( mnHeight > rOther.mnHeight )
        return COMPARE_GREATER;

    if( mnWidth < rOther.mnWidth )
        return COMPARE_LESS;
    else if( mnWidth > rOther.mnWidth )
        return COMPARE_GREATER;

    return COMPARE_EQUAL;
}
// =======================================================================

ImplDevFontListData::ImplDevFontListData( const String& rSearchName )
:   mpFirst( NULL ),
    maSearchName( rSearchName ),
    mnTypeFaces( 0 ),
    mnMatchType( 0 ),
    meMatchWeight( WEIGHT_DONTKNOW ),
    meMatchWidth( WIDTH_DONTKNOW ),
    meFamily( FAMILY_DONTKNOW ),
    mePitch( PITCH_DONTKNOW ),
    mnMinQuality( -1 )
{}

// -----------------------------------------------------------------------

ImplDevFontListData::~ImplDevFontListData()
{
    // release all physical font faces
    while( mpFirst )
    {
        ImplFontData* pFace = mpFirst;
        mpFirst = pFace->GetNextFace();
        delete pFace;
    }
}
// -----------------------------------------------------------------------

bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData )
{
    pNewData->mpNext = NULL;

    if( !mpFirst )
    {
        maName         = pNewData->maName;
        maMapNames     = pNewData->maMapNames;
        meFamily       = pNewData->meFamily;
        mePitch        = pNewData->mePitch;
        mnMinQuality   = pNewData->mnQuality;
    }
    else
    {
        if( meFamily == FAMILY_DONTKNOW )
            meFamily = pNewData->meFamily;
        if( mePitch == PITCH_DONTKNOW )
            mePitch = pNewData->mePitch;
        if( mnMinQuality > pNewData->mnQuality )
            mnMinQuality = pNewData->mnQuality;
    }

    // set attributes for attribute based font matching
    if( pNewData->IsScalable() )
        mnTypeFaces |= IMPL_DEVFONT_SCALABLE;

    if( pNewData->IsSymbolFont() )
        mnTypeFaces |= IMPL_DEVFONT_SYMBOL;
    else
        mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL;

    if( pNewData->meWeight != WEIGHT_DONTKNOW )
    {
        if( pNewData->meWeight >= WEIGHT_SEMIBOLD )
            mnTypeFaces |= IMPL_DEVFONT_BOLD;
        else if( pNewData->meWeight <= WEIGHT_SEMILIGHT )
            mnTypeFaces |= IMPL_DEVFONT_LIGHT;
        else
            mnTypeFaces |= IMPL_DEVFONT_NORMAL;
    }

    if( pNewData->meItalic == ITALIC_NONE )
        mnTypeFaces |= IMPL_DEVFONT_NONEITALIC;
    else if( (pNewData->meItalic == ITALIC_NORMAL)
         ||  (pNewData->meItalic == ITALIC_OBLIQUE) )
        mnTypeFaces |= IMPL_DEVFONT_ITALIC;

    if( (meMatchWeight == WEIGHT_DONTKNOW)
    ||  (meMatchWidth  == WIDTH_DONTKNOW)
    ||  (mnMatchType   == 0) )
    {
        // TODO: is it cheaper to calc matching attributes now or on demand?
        // calc matching attributes if other entries are already initialized

        // MT: Perform05: Do lazy, quite expensive, not needed in start-up!
        // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
        // InitMatchData( rFontSubst, maSearchName );
		// mbMatchData=true; // Somewhere else???
    }

    // reassign name (sharing saves memory)
    if( pNewData->maName == maName )
        pNewData->maName = maName;

    // insert new physical font face into linked list
    // TODO: get rid of linear search?
    ImplFontData* pData;
    ImplFontData** ppHere = &mpFirst;

    for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext )
    {
        StringCompare eComp = pNewData->CompareWithSize( *pData );
        if( eComp == COMPARE_GREATER )
            continue;
        if( eComp == COMPARE_LESS )
            break;

        // ignore duplicate if its quality is worse
        if( pNewData->mnQuality < pData->mnQuality )
            return false;

        // keep the device font if its quality is good enough
        if( (pNewData->mnQuality == pData->mnQuality)
        &&  (pData->mbDevice || !pNewData->mbDevice) )
            return false;

        // replace existing font face with a better one
        pNewData->mpNext = pData->mpNext;
        *ppHere = pNewData;
        delete pData;
        return true;
    }

    // insert into or append to list of physical font faces
    pNewData->mpNext = pData;
    *ppHere = pNewData;
    return true;
}
// -----------------------------------------------------------------------

ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const
{
    if( !mpFirst )
        return NULL;
    if( !mpFirst->GetNextFace() )
        return mpFirst;

    // FontName+StyleName should map to FamilyName+StyleName
    const String& rSearchName = rFSD.maTargetName;
    const xub_Unicode* pTargetStyleName = NULL;
    if( (rSearchName.Len() > maSearchName.Len())
    &&   rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) )
        pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1;

    // linear search, TODO: improve?
    ImplFontData* pFontFace = mpFirst;
    ImplFontData* pBestFontFace = pFontFace;
    FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName};
    for(; pFontFace; pFontFace = pFontFace->GetNextFace() )
        if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) )
            pBestFontFace = pFontFace;

    return pBestFontFace;
}

// =======================================================================

ImplFontSelectData::ImplFontSelectData( const Font& rFont,
    const String& rSearchName, const Size& rSize, float fExactHeight)
:   maSearchName( rSearchName ),
    mnWidth( rSize.Width() ),
    mnHeight( rSize.Height() ),
    mfExactHeight( fExactHeight),
    mnOrientation( rFont.GetOrientation() ),
    meLanguage( rFont.GetLanguage() ),
    mbVertical( rFont.IsVertical() ),
    mbNonAntialiased( false ),
    mpFontData( NULL ),
    mpFontEntry( NULL )
{
    maTargetName = maName;

    rFont.GetFontAttributes( *this );

    // normalize orientation between 0 and 3600
    if( 3600 <= (unsigned)mnOrientation )
    {
        if( mnOrientation >= 0 )
            mnOrientation %= 3600;
        else
            mnOrientation = 3600 - (-mnOrientation % 3600);
    }

    // normalize width and height
    if( mnHeight < 0 )
        mnHeight = -mnHeight;
    if( mnWidth < 0 )
        mnWidth = -mnWidth;
}

// -----------------------------------------------------------------------

ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData,
    const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
:   ImplFontAttributes( rFontData ),
    mnWidth( rSize.Width() ),
    mnHeight( rSize.Height() ),
    mfExactHeight( fExactHeight ),
    mnOrientation( nOrientation ),
    meLanguage( 0 ),
    mbVertical( bVertical ),
    mbNonAntialiased( false ),
    mpFontData( &rFontData ),
    mpFontEntry( NULL )
{
    maTargetName = maSearchName = maName;
    // NOTE: no normalization for width/height/orientation
}