/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

#include "emfwr.hxx"
#include <vcl/salbtype.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/rendergraphicrasterizer.hxx>

// -----------
// - Defines -
// -----------

#define WIN_EMR_HEADER                      1
#define WIN_EMR_POLYBEZIER                  2
#define WIN_EMR_POLYGON                     3
#define WIN_EMR_POLYLINE                    4
#define WIN_EMR_POLYBEZIERTO                5
#define WIN_EMR_POLYLINETO                  6
#define WIN_EMR_POLYPOLYLINE                7
#define WIN_EMR_POLYPOLYGON                 8
#define WIN_EMR_SETWINDOWEXTEX              9
#define WIN_EMR_SETWINDOWORGEX              10
#define WIN_EMR_SETVIEWPORTEXTEX            11
#define WIN_EMR_SETVIEWPORTORGEX            12
#define WIN_EMR_SETBRUSHORGEX               13
#define WIN_EMR_EOF                         14
#define WIN_EMR_SETPIXELV                   15
#define WIN_EMR_SETMAPPERFLAGS              16
#define WIN_EMR_SETMAPMODE                  17
#define WIN_EMR_SETBKMODE                   18
#define WIN_EMR_SETPOLYFILLMODE             19
#define WIN_EMR_SETROP2                     20
#define WIN_EMR_SETSTRETCHBLTMODE           21
#define WIN_EMR_SETTEXTALIGN                22
#define WIN_EMR_SETCOLORADJUSTMENT          23
#define WIN_EMR_SETTEXTCOLOR                24
#define WIN_EMR_SETBKCOLOR                  25
#define WIN_EMR_OFFSETCLIPRGN               26
#define WIN_EMR_MOVETOEX                    27
#define WIN_EMR_SETMETARGN                  28
#define WIN_EMR_EXCLUDECLIPRECT             29
#define WIN_EMR_INTERSECTCLIPRECT           30
#define WIN_EMR_SCALEVIEWPORTEXTEX          31
#define WIN_EMR_SCALEWINDOWEXTEX            32
#define WIN_EMR_SAVEDC                      33
#define WIN_EMR_RESTOREDC                   34
#define WIN_EMR_SETWORLDTRANSFORM           35
#define WIN_EMR_MODIFYWORLDTRANSFORM        36
#define WIN_EMR_SELECTOBJECT                37
#define WIN_EMR_CREATEPEN                   38
#define WIN_EMR_CREATEBRUSHINDIRECT         39
#define WIN_EMR_DELETEOBJECT                40
#define WIN_EMR_ANGLEARC                    41
#define WIN_EMR_ELLIPSE                     42
#define WIN_EMR_RECTANGLE                   43
#define WIN_EMR_ROUNDRECT                   44
#define WIN_EMR_ARC                         45
#define WIN_EMR_CHORD                       46
#define WIN_EMR_PIE                         47
#define WIN_EMR_SELECTPALETTE               48
#define WIN_EMR_CREATEPALETTE               49
#define WIN_EMR_SETPALETTEENTRIES           50
#define WIN_EMR_RESIZEPALETTE               51
#define WIN_EMR_REALIZEPALETTE              52
#define WIN_EMR_EXTFLOODFILL                53
#define WIN_EMR_LINETO                      54
#define WIN_EMR_ARCTO                       55
#define WIN_EMR_POLYDRAW                    56
#define WIN_EMR_SETARCDIRECTION             57
#define WIN_EMR_SETMITERLIMIT               58
#define WIN_EMR_BEGINPATH                   59
#define WIN_EMR_ENDPATH                     60
#define WIN_EMR_CLOSEFIGURE                 61
#define WIN_EMR_FILLPATH                    62
#define WIN_EMR_STROKEANDFILLPATH           63
#define WIN_EMR_STROKEPATH                  64
#define WIN_EMR_FLATTENPATH                 65
#define WIN_EMR_WIDENPATH                   66
#define WIN_EMR_SELECTCLIPPATH              67
#define WIN_EMR_ABORTPATH                   68

#define WIN_EMR_GDICOMMENT                  70
#define WIN_EMR_FILLRGN                     71
#define WIN_EMR_FRAMERGN                    72
#define WIN_EMR_INVERTRGN                   73
#define WIN_EMR_PAINTRGN                    74
#define WIN_EMR_EXTSELECTCLIPRGN            75
#define WIN_EMR_BITBLT                      76
#define WIN_EMR_STRETCHBLT                  77
#define WIN_EMR_MASKBLT                     78
#define WIN_EMR_PLGBLT                      79
#define WIN_EMR_SETDIBITSTODEVICE           80
#define WIN_EMR_STRETCHDIBITS               81
#define WIN_EMR_EXTCREATEFONTINDIRECTW      82
#define WIN_EMR_EXTTEXTOUTA                 83
#define WIN_EMR_EXTTEXTOUTW                 84
#define WIN_EMR_POLYBEZIER16                85
#define WIN_EMR_POLYGON16                   86
#define WIN_EMR_POLYLINE16                  87
#define WIN_EMR_POLYBEZIERTO16              88
#define WIN_EMR_POLYLINETO16                89
#define WIN_EMR_POLYPOLYLINE16              90
#define WIN_EMR_POLYPOLYGON16               91
#define WIN_EMR_POLYDRAW16                  92
#define WIN_EMR_CREATEMONOBRUSH             93
#define WIN_EMR_CREATEDIBPATTERNBRUSHPT     94
#define WIN_EMR_EXTCREATEPEN                95
#define WIN_EMR_POLYTEXTOUTA                96
#define WIN_EMR_POLYTEXTOUTW                97

#define WIN_SRCCOPY							0x00CC0020L
#define WIN_SRCPAINT						0x00EE0086L
#define WIN_SRCAND							0x008800C6L
#define WIN_SRCINVERT						0x00660046L

#define HANDLE_INVALID						0xffffffff
#define MAXHANDLES							65000

#define LINE_SELECT							0x00000001
#define FILL_SELECT							0x00000002
#define TEXT_SELECT							0x00000004

/* Text Alignment Options */
#define TA_NOUPDATECP						0
#define TA_UPDATECP							1

#define TA_LEFT								0
#define TA_RIGHT							2
#define TA_CENTER							6

#define TA_TOP								0
#define TA_BOTTOM							8
#define TA_BASELINE							24
#define TA_RTLREADING						256
#define TA_MASK		(TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING)

#define MM_ANISOTROPIC						8

// -------------
// - EMFWriter -
// -------------

sal_Bool EMFWriter::WriteEMF( const GDIMetaFile& rMtf, SvStream& rOStm, FilterConfigItem* pFilterConfigItem )
{
	const sal_uLong nHeaderPos = rOStm.Tell();

	mpHandlesUsed = new sal_Bool[ MAXHANDLES ];
	memset( mpHandlesUsed, 0, MAXHANDLES * sizeof( sal_Bool ) );
	mnHorTextAlign = mnHandleCount = mnLastPercent = mnRecordPos = mnRecordCount = 0;
	mnLineHandle = mnFillHandle = mnTextHandle = HANDLE_INVALID;
	mbRecordOpen = sal_False;

	mpStm = &rOStm;
	maVDev.EnableOutput( sal_False );
	maVDev.SetMapMode( rMtf.GetPrefMapMode() );
	mpFilterConfigItem = pFilterConfigItem;

	// don't work with pixel as destination map mode -> higher resolution preferrable
	maDestMapMode.SetMapUnit( MAP_100TH_MM );

	const Size aMtfSizePix( maVDev.LogicToPixel( rMtf.GetPrefSize(), rMtf.GetPrefMapMode() ) );
	const Size aMtfSizeLog( maVDev.LogicToLogic( rMtf.GetPrefSize(), rMtf.GetPrefMapMode(), MAP_100TH_MM ) );

	// seek over header
	// use [MS-EMF 2.2.11] HeaderExtension2 Object, otherwise resulting EMF cannot be converted with GetWinMetaFileBits()
	rOStm.SeekRel( 108 );

	// write initial values

	// set 100th mm map mode in EMF
	ImplBeginRecord( WIN_EMR_SETMAPMODE );
	(*mpStm) << (sal_Int32) MM_ANISOTROPIC;
	ImplEndRecord();

	ImplBeginRecord( WIN_EMR_SETVIEWPORTEXTEX );
	(*mpStm) << (sal_Int32) maVDev.ImplGetDPIX() << (sal_Int32) maVDev.ImplGetDPIY();
	ImplEndRecord();

	ImplBeginRecord( WIN_EMR_SETWINDOWEXTEX );
	(*mpStm) << (sal_Int32) 2540 << (sal_Int32) 2540;
	ImplEndRecord();

	ImplBeginRecord( WIN_EMR_SETVIEWPORTORGEX );
	(*mpStm) << (sal_Int32) 0 << (sal_Int32) 0;
	ImplEndRecord();

	ImplBeginRecord( WIN_EMR_SETWINDOWORGEX );
	(*mpStm) << (sal_Int32) 0 << (sal_Int32) 0;
	ImplEndRecord();

	ImplWriteRasterOp( ROP_OVERPAINT );

	ImplBeginRecord( WIN_EMR_SETBKMODE );
	(*mpStm) << (sal_uInt32) 1; // TRANSPARENT
	ImplEndRecord();

	// write emf data
	ImplWrite( rMtf );

	ImplBeginRecord( WIN_EMR_EOF );
	(*mpStm)<< (sal_uInt32)0		// nPalEntries
			<< (sal_uInt32)0x10		// offPalEntries
			<< (sal_uInt32)0x14;	// nSizeLast
	ImplEndRecord();


	// write header
	const sal_uLong nEndPos = mpStm->Tell(); mpStm->Seek( nHeaderPos );

	(*mpStm) << (sal_uInt32) 0x00000001 << (sal_uInt32) 108	//use [MS-EMF 2.2.11] HeaderExtension2 Object
			 << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) ( aMtfSizePix.Width() - 1 ) << (sal_Int32) ( aMtfSizePix.Height() - 1 )
		     << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) ( aMtfSizeLog.Width() - 1 ) << (sal_Int32) ( aMtfSizeLog.Height() - 1 )
			 << (sal_uInt32) 0x464d4520 << (sal_uInt32) 0x10000 << (sal_uInt32) ( nEndPos - nHeaderPos )
			 << (sal_uInt32) mnRecordCount << (sal_uInt16) ( mnHandleCount + 1 ) << (sal_uInt16) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0
			 << (sal_Int32) aMtfSizePix.Width() << (sal_Int32) aMtfSizePix.Height()
			 << (sal_Int32) ( aMtfSizeLog.Width() / 100 ) << (sal_Int32) ( aMtfSizeLog.Height() / 100 )
			 << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0
			 << (sal_Int32) (  aMtfSizeLog.Width() * 10 ) << (sal_Int32) ( aMtfSizeLog.Height() * 10 ); //use [MS-EMF 2.2.11] HeaderExtension2 Object

	mpStm->Seek( nEndPos );
	delete[] mpHandlesUsed;

	return( mpStm->GetError() == ERRCODE_NONE );
}

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

sal_uLong EMFWriter::ImplAcquireHandle()
{
	sal_uLong nHandle = HANDLE_INVALID;

	for( sal_uLong i = 0; i < MAXHANDLES && ( HANDLE_INVALID == nHandle ); i++ )
	{
		if( !mpHandlesUsed[ i ] )
		{
			mpHandlesUsed[ i ] = sal_True;

			if( ( nHandle = i ) == mnHandleCount )
				mnHandleCount++;
		}
	}

	DBG_ASSERT( nHandle != HANDLE_INVALID, "No more handles available" );
	return( nHandle != HANDLE_INVALID ? nHandle + 1 : HANDLE_INVALID );
}

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

void EMFWriter::ImplReleaseHandle( sal_uLong nHandle )
{
	DBG_ASSERT( nHandle && ( nHandle < MAXHANDLES ), "Handle out of range" );
	mpHandlesUsed[ nHandle - 1 ] = sal_False;
}

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

void EMFWriter::ImplBeginRecord( sal_uInt32 nType )
{
	DBG_ASSERT( !mbRecordOpen, "Another record is already opened!" );

	if( !mbRecordOpen )
	{
		mbRecordOpen = sal_True;
		mnRecordPos = mpStm->Tell();

		(*mpStm) << nType;
		mpStm->SeekRel( 4 );
	}
}

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

void EMFWriter::ImplEndRecord()
{
	DBG_ASSERT( mbRecordOpen, "Record was not opened!" );

	if( mbRecordOpen )
	{
		sal_Int32 nFillBytes, nActPos = mpStm->Tell();
		mpStm->Seek( mnRecordPos + 4 );
		nFillBytes = nActPos - mnRecordPos;
		nFillBytes += 3;	// each record has to be dword aligned
		nFillBytes ^= 3;
		nFillBytes &= 3;
		*mpStm << (sal_uInt32)( ( nActPos - mnRecordPos ) + nFillBytes );
		mpStm->Seek( nActPos );
		while( nFillBytes-- )
			*mpStm << (sal_uInt8)0;
		mnRecordCount++;
		mbRecordOpen = sal_False;
	}
}

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

sal_Bool EMFWriter::ImplPrepareHandleSelect( sal_uInt32& rHandle, sal_uLong nSelectType )
{
	if( rHandle != HANDLE_INVALID )
	{
		sal_uInt32 nStockObject = 0x80000000;

		if( LINE_SELECT == nSelectType )
			nStockObject |= 0x00000007;
		else if( FILL_SELECT == nSelectType )
			nStockObject |= 0x00000001;
		else if( TEXT_SELECT == nSelectType )
			nStockObject |= 0x0000000a;

		// select stock object first
		ImplBeginRecord( WIN_EMR_SELECTOBJECT );
		( *mpStm ) << nStockObject;
		ImplEndRecord();

		// destroy handle of created object
		ImplBeginRecord( WIN_EMR_DELETEOBJECT );
		( *mpStm ) << rHandle;
		ImplEndRecord();

		// mark handle as free
		ImplReleaseHandle( rHandle );
	}

	rHandle = ImplAcquireHandle();

	return( HANDLE_INVALID != rHandle );
}

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

void EMFWriter::ImplCheckLineAttr()
{
	if( mbLineChanged && ImplPrepareHandleSelect( mnLineHandle, LINE_SELECT ) )
	{
		sal_uInt32 nStyle = maVDev.IsLineColor() ? 0 : 5;
		sal_uInt32 nWidth = 0, nHeight = 0;

		ImplBeginRecord( WIN_EMR_CREATEPEN );
		(*mpStm) << mnLineHandle << nStyle << nWidth << nHeight;
		ImplWriteColor( maVDev.GetLineColor() );
		ImplEndRecord();

		ImplBeginRecord( WIN_EMR_SELECTOBJECT );
		(*mpStm) << mnLineHandle;
		ImplEndRecord();
	}
}

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

void EMFWriter::ImplCheckFillAttr()
{
	if( mbFillChanged && ImplPrepareHandleSelect( mnFillHandle, FILL_SELECT ) )
	{
		sal_uInt32 nStyle = maVDev.IsFillColor() ? 0 : 1;
		sal_uInt32 nPatternStyle = 0;

		ImplBeginRecord( WIN_EMR_CREATEBRUSHINDIRECT );
		(*mpStm) << mnFillHandle << nStyle;
		ImplWriteColor( maVDev.GetFillColor() );
		(*mpStm) << nPatternStyle;
		ImplEndRecord();

		ImplBeginRecord( WIN_EMR_SELECTOBJECT );
		(*mpStm) << mnFillHandle;
		ImplEndRecord();
	}
}

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

void EMFWriter::ImplCheckTextAttr()
{
	if( mbTextChanged && ImplPrepareHandleSelect( mnTextHandle, TEXT_SELECT ) )
	{
		const Font&		rFont = maVDev.GetFont();
		String			aFontName( rFont.GetName() );
		sal_Int32		nWeight;
		sal_uInt16		i;
		sal_uInt8		nPitchAndFamily;

		ImplBeginRecord( WIN_EMR_EXTCREATEFONTINDIRECTW );
		(*mpStm) << mnTextHandle;
		ImplWriteExtent( -rFont.GetSize().Height() );
		ImplWriteExtent( rFont.GetSize().Width() );
		(*mpStm) << (sal_Int32) rFont.GetOrientation() << (sal_Int32) rFont.GetOrientation();

		switch( rFont.GetWeight() )
		{
			case WEIGHT_THIN:       nWeight = 100; break;
			case WEIGHT_ULTRALIGHT: nWeight = 200; break;
			case WEIGHT_LIGHT:      nWeight = 300; break;
			case WEIGHT_SEMILIGHT:  nWeight = 300; break;
			case WEIGHT_NORMAL:     nWeight = 400; break;
			case WEIGHT_MEDIUM:     nWeight = 500; break;
			case WEIGHT_SEMIBOLD:   nWeight = 600; break;
			case WEIGHT_BOLD:       nWeight = 700; break;
			case WEIGHT_ULTRABOLD:  nWeight = 800; break;
			case WEIGHT_BLACK:      nWeight = 900; break;
			default:				nWeight = 0; break;
		}

		(*mpStm) << nWeight;
		(*mpStm) << (sal_uInt8) ( ( ITALIC_NONE == rFont.GetItalic() ) ? 0 : 1 );
		(*mpStm) << (sal_uInt8) ( ( UNDERLINE_NONE == rFont.GetUnderline() ) ? 0 : 1 );
		(*mpStm) << (sal_uInt8) ( ( STRIKEOUT_NONE == rFont.GetStrikeout() ) ? 0 : 1 );
		(*mpStm) << (sal_uInt8) ( ( RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet() ) ? 2 : 0 );
		(*mpStm) << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0;

		switch( rFont.GetPitch() )
		{
			case PITCH_FIXED:    nPitchAndFamily = 0x01; break;
			case PITCH_VARIABLE: nPitchAndFamily = 0x02; break;
			default:             nPitchAndFamily = 0x00; break;
		}

		switch( rFont.GetFamily() )
		{
			case FAMILY_DECORATIVE: nPitchAndFamily |= 0x50; break;
			case FAMILY_MODERN:     nPitchAndFamily |= 0x30; break;
			case FAMILY_ROMAN:      nPitchAndFamily |= 0x10; break;
			case FAMILY_SCRIPT:     nPitchAndFamily |= 0x40; break;
			case FAMILY_SWISS:      nPitchAndFamily |= 0x20; break;
			default: break;
		}

		(*mpStm) << nPitchAndFamily;

		for( i = 0; i < 32; i++ )
			(*mpStm) << (sal_Unicode) ( ( i < aFontName.Len() ) ? aFontName.GetChar( i ) : 0 );

		// dummy elfFullName
		for( i = 0; i < 64; i++ )
			(*mpStm) << (sal_Unicode) 0;

		// dummy elfStyle
		for( i = 0; i < 32; i++ )
			(*mpStm) << (sal_Unicode) 0;

		// dummy elfVersion, elfStyleSize, elfMatch, elfReserved
		(*mpStm) << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 ;

		// dummy elfVendorId
		(*mpStm) << (sal_uInt32) 0;

		// dummy elfCulture
		(*mpStm) << (sal_uInt32) 0;

		// dummy elfPanose
		(*mpStm) << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0;

		// fill record to get a record size divideable by 4
		(*mpStm) << (sal_uInt16) 0;

		ImplEndRecord();

		// TextAlign
		sal_uInt32 nTextAlign;

		switch( rFont.GetAlign() )
		{
			case ALIGN_TOP:    nTextAlign = TA_TOP; break;
			case ALIGN_BOTTOM: nTextAlign = TA_BOTTOM; break;
			default:           nTextAlign = TA_BASELINE; break;
		}
		nTextAlign |= mnHorTextAlign;

		ImplBeginRecord( WIN_EMR_SETTEXTALIGN );
		(*mpStm) << nTextAlign;
		ImplEndRecord();

		// Text color
		ImplBeginRecord( WIN_EMR_SETTEXTCOLOR );
		ImplWriteColor( maVDev.GetTextColor() );
		ImplEndRecord();

		ImplBeginRecord( WIN_EMR_SELECTOBJECT );
		(*mpStm) << mnTextHandle;
		ImplEndRecord();
	}
}

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

void EMFWriter::ImplWriteColor( const Color& rColor )
{
	sal_uInt32 nCol = rColor.GetRed();

	nCol |= ( (sal_uInt32) rColor.GetGreen() ) << 8;
	nCol |= ( (sal_uInt32) rColor.GetBlue() ) << 16;

	(*mpStm) << nCol;
}

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

void EMFWriter::ImplWriteRasterOp( RasterOp eRop )
{
	sal_uInt32 nROP2;

	switch( eRop )
	{
		case ROP_INVERT: nROP2 = 6;	break;
		case ROP_XOR:    nROP2 = 7;	break;
		default:         nROP2 = 13;break;
	}

	ImplBeginRecord( WIN_EMR_SETROP2 );
	(*mpStm) << nROP2;
	ImplEndRecord();
}

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

void EMFWriter::ImplWriteExtent( long nExtent )
{
	nExtent = maVDev.LogicToLogic( Size( nExtent, 0 ), maVDev.GetMapMode(), maDestMapMode ).Width();
	(*mpStm) << (sal_Int32) nExtent;
}

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

void EMFWriter::ImplWritePoint( const Point& rPoint )
{
	const Point aPoint( maVDev.LogicToLogic( rPoint, maVDev.GetMapMode(), maDestMapMode ));
 	(*mpStm) << (sal_Int32) aPoint.X() << (sal_Int32) aPoint.Y();
}

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

void EMFWriter::ImplWriteSize( const Size& rSize)
{
	const Size aSize( maVDev.LogicToLogic( rSize, maVDev.GetMapMode(), maDestMapMode ));
 	(*mpStm) << (sal_Int32) aSize.Width() << (sal_Int32) aSize.Height();
}

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

void EMFWriter::ImplWriteRect( const Rectangle& rRect )
{
	const Rectangle aRect( maVDev.LogicToLogic ( rRect, maVDev.GetMapMode(), maDestMapMode ));
 	(*mpStm) << aRect.Left() << aRect.Top() << aRect.Right() << aRect.Bottom();
}

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

void EMFWriter::ImplWritePolygonRecord( const Polygon& rPoly, sal_Bool bClose )
{
	if( rPoly.GetSize() )
	{
		if( rPoly.HasFlags() )
			ImplWritePath( rPoly, bClose );
		else
		{
			if( bClose )
				ImplCheckFillAttr();

			ImplCheckLineAttr();

			ImplBeginRecord( bClose ? WIN_EMR_POLYGON : WIN_EMR_POLYLINE );
			ImplWriteRect( rPoly.GetBoundRect() );
			(*mpStm) << (sal_uInt32) rPoly.GetSize();

			for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ )
				ImplWritePoint( rPoly[ i ] );

			ImplEndRecord();
		}
	}
}

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

void EMFWriter::ImplWritePolyPolygonRecord( const PolyPolygon& rPolyPoly )
{
	sal_uInt16 n, i, nPolyCount = rPolyPoly.Count();

	if( nPolyCount )
	{
		if( 1 == nPolyCount )
			ImplWritePolygonRecord( rPolyPoly[ 0 ], sal_True );
		else
		{
			sal_Bool	bHasFlags = sal_False;
			sal_uInt32	nTotalPoints = 0;

			for( i = 0; i < nPolyCount; i++ )
			{
				nTotalPoints += rPolyPoly[ i ].GetSize();
				if ( rPolyPoly[ i ].HasFlags() )
					bHasFlags = sal_True;
			}
			if( nTotalPoints )
			{
				if ( bHasFlags )
					ImplWritePath( rPolyPoly, sal_True );
				else
				{
					ImplCheckFillAttr();
					ImplCheckLineAttr();

					ImplBeginRecord( WIN_EMR_POLYPOLYGON );
					ImplWriteRect( rPolyPoly.GetBoundRect() );
					(*mpStm) << (sal_uInt32)nPolyCount << nTotalPoints;

					for( i = 0; i < nPolyCount; i++ )
						(*mpStm) << (sal_uInt32)rPolyPoly[ i ].GetSize();

					for( i = 0; i < nPolyCount; i++ )
					{
						const Polygon& rPoly = rPolyPoly[ i ];

						for( n = 0; n < rPoly.GetSize(); n++ )
							ImplWritePoint( rPoly[ n ] );
					}
					ImplEndRecord();
				}
			}
		}
	}
}

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

void EMFWriter::ImplWritePath( const PolyPolygon& rPolyPoly, sal_Bool bClosed )
{
	if ( bClosed )
		ImplCheckFillAttr();
	ImplCheckLineAttr();

	ImplBeginRecord( WIN_EMR_BEGINPATH );
	ImplEndRecord();

	sal_uInt16 i, n, o, nPolyCount = rPolyPoly.Count();
	for ( i = 0; i < nPolyCount; i++ )
	{
		n = 0;
		const Polygon& rPoly = rPolyPoly[ i ];
		while ( n < rPoly.GetSize() )
		{
            if( n == 0 )
            {
		        ImplBeginRecord( WIN_EMR_MOVETOEX );
		        ImplWritePoint( rPoly[ 0 ] );
		        ImplEndRecord();
                n++;
                continue;
            }

            sal_uInt16 nBezPoints = 0;

			while ( ( ( nBezPoints + n + 2 ) < rPoly.GetSize() ) && ( rPoly.GetFlags( nBezPoints + n ) == POLY_CONTROL ) )
				nBezPoints += 3;

			if ( nBezPoints )
			{
				ImplBeginRecord( WIN_EMR_POLYBEZIERTO );
				Polygon aNewPoly( nBezPoints + 1 );
				aNewPoly[ 0 ] = rPoly[ n - 1 ];
				for ( o = 0; o < nBezPoints; o++ )
					aNewPoly[ o + 1 ] = rPoly[ n + o ];
				ImplWriteRect( aNewPoly.GetBoundRect() );
				(*mpStm) << (sal_uInt32)nBezPoints;
				for( o = 1; o < aNewPoly.GetSize(); o++ )
					ImplWritePoint( aNewPoly[ o ] );
				ImplEndRecord();
				n = n + nBezPoints;
			}
			else
			{
				sal_uInt16 nPoints = 1;
				while( ( nPoints + n ) < rPoly.GetSize() && ( rPoly.GetFlags( nPoints + n ) != POLY_CONTROL ) )
					nPoints++;

				if ( nPoints > 1 )
				{
					ImplBeginRecord( WIN_EMR_POLYLINETO );
					Polygon aNewPoly( nPoints + 1 );
					aNewPoly[ 0 ] = rPoly[ n - 1];
					for ( o = 1; o <= nPoints; o++ )
						aNewPoly[ o ] = rPoly[ n - 1 + o ];
					ImplWriteRect( aNewPoly.GetBoundRect() );
					(*mpStm) << (sal_uInt32)( nPoints );
					for( o = 1; o < aNewPoly.GetSize(); o++ )
						ImplWritePoint( aNewPoly[ o ] );
					ImplEndRecord();
				}
                else
                {
                    ImplBeginRecord( WIN_EMR_LINETO );
                    ImplWritePoint( rPoly[ n ] );
                    ImplEndRecord();
                }
                n = n + nPoints;
			}
			if ( bClosed && ( n == rPoly.GetSize() ) )
			{
				ImplBeginRecord( WIN_EMR_CLOSEFIGURE );
				ImplEndRecord();
			}
		}
	}
	ImplBeginRecord( WIN_EMR_ENDPATH );
	ImplEndRecord();
	ImplBeginRecord( bClosed ? WIN_EMR_FILLPATH : WIN_EMR_STROKEPATH );
    ImplWriteRect( rPolyPoly.GetBoundRect() );
	ImplEndRecord();
}

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

void EMFWriter::ImplWriteBmpRecord( const Bitmap& rBmp, const Point& rPt,
									const Size& rSz, sal_uInt32 nROP )
{
	if( !!rBmp )
	{
		SvMemoryStream	aMemStm( 65535, 65535 );
		const Size		aBmpSizePixel( rBmp.GetSizePixel() );

		ImplBeginRecord( WIN_EMR_STRETCHDIBITS );
		ImplWriteRect( Rectangle( rPt, rSz ) );
		ImplWritePoint( rPt );
		(*mpStm) << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) aBmpSizePixel.Width() << (sal_Int32) aBmpSizePixel.Height();

		// write offset positions and sizes later
		const sal_uLong nOffPos = mpStm->Tell();
		mpStm->SeekRel( 16 );

		(*mpStm) << (sal_uInt32) 0 << ( ( ROP_XOR == maVDev.GetRasterOp() && WIN_SRCCOPY == nROP ) ? WIN_SRCINVERT : nROP );
		ImplWriteSize( rSz );

		rBmp.Write( aMemStm, sal_True, sal_False );

		sal_uInt32	nDIBSize = aMemStm.Tell(), nHeaderSize, nCompression, nColsUsed, nPalCount, nImageSize;
		sal_uInt16	nBitCount;

		// get DIB parameters
		aMemStm.Seek( 0 );
		aMemStm >> nHeaderSize;
		aMemStm.SeekRel( 10 );
		aMemStm >> nBitCount >> nCompression >> nImageSize;
		aMemStm.SeekRel( 8 );
		aMemStm >> nColsUsed;

		nPalCount = ( nBitCount <= 8 ) ? ( nColsUsed ? nColsUsed : ( 1 << (sal_uInt32) nBitCount ) ) :
										 ( ( 3 == nCompression ) ? 12 : 0 );

		mpStm->Write( aMemStm.GetData(), nDIBSize );

		const sal_uLong nEndPos = mpStm->Tell();
		mpStm->Seek( nOffPos );
		(*mpStm) << (sal_uInt32) 80 << (sal_uInt32)( nHeaderSize + ( nPalCount << 2 ) );
		(*mpStm) << (sal_uInt32)( 80 + ( nHeaderSize + ( nPalCount << 2 ) ) ) << nImageSize;
		mpStm->Seek( nEndPos );

		ImplEndRecord();
	}
}

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

void EMFWriter::ImplWriteTextRecord( const Point& rPos, const String rText, const sal_Int32* pDXArray, sal_uInt32 nWidth )
{
	xub_StrLen nLen = rText.Len(), i;

	if( nLen )
	{
		sal_uInt32	nNormWidth;
		sal_Int32*	pOwnArray;
		sal_Int32*	pDX;

		// get text sizes
		if( pDXArray )
		{
			pOwnArray = NULL;
			nNormWidth = maVDev.GetTextWidth( rText );
			pDX = (sal_Int32*) pDXArray;
		}
		else
		{
			pOwnArray = new sal_Int32[ nLen ];
			nNormWidth = maVDev.GetTextArray( rText, pOwnArray );
			pDX = pOwnArray;
		}

		if( nLen > 1 )
		{
			nNormWidth = pDX[ nLen - 2 ] + maVDev.GetTextWidth( rText.GetChar( nLen - 1 ) );

			if( nWidth && nNormWidth && ( nWidth != nNormWidth ) )
			{
				const double fFactor = (double) nWidth / nNormWidth;

				for( i = 0; i < ( nLen - 1 ); i++ )
					pDX[ i ] = FRound( pDX[ i ] * fFactor );
			}
		}

		// write text record
		ImplBeginRecord( WIN_EMR_EXTTEXTOUTW );

		ImplWriteRect( Rectangle( rPos, Size( nNormWidth, maVDev.GetTextHeight() ) ) );
		(*mpStm) << (sal_uInt32)1;
		(*mpStm) << (sal_Int32) 0 << (sal_Int32) 0;
		ImplWritePoint( rPos );
		(*mpStm) << (sal_uInt32) nLen << (sal_uInt32) 76 << (sal_uInt32) 2;
		(*mpStm) << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) 0;
		(*mpStm) << (sal_uInt32) ( 76 + ( nLen << 1 ) + ( (nLen & 1 ) ? 2 : 0 ) );

		// write text
		for( i = 0; i < nLen; i++ )
			(*mpStm) << (sal_Unicode)rText.GetChar( i );

		// padding word
		if( nLen & 1 )
			(*mpStm) << (sal_uInt16) 0;

		// write DX array
		ImplWriteExtent( pDX[ 0 ] );

		if( nLen > 1 )
		{
			for( i = 1; i < ( nLen - 1 ); i++ )
				ImplWriteExtent( pDX[ i ] - pDX[ i - 1 ] );

			ImplWriteExtent( pDX[ nLen - 2 ] / ( nLen - 1 ) );
		}

		ImplEndRecord();
		delete[] pOwnArray;
	}
}

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

void EMFWriter::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
{
	if(rLinePolygon.count())
	{
		basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
		basegfx::B2DPolyPolygon aFillPolyPolygon;

		rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);

		if(aLinePolyPolygon.count())
		{
			for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
			{
				const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
    			ImplWritePolygonRecord( Polygon(aCandidate), sal_False );
			}
		}

		if(aFillPolyPolygon.count())
		{
			const Color aOldLineColor(maVDev.GetLineColor());
			const Color aOldFillColor(maVDev.GetFillColor());

			maVDev.SetLineColor();
			maVDev.SetFillColor(aOldLineColor);

			for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
			{
				const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
    			ImplWritePolyPolygonRecord(PolyPolygon(Polygon(aPolygon)));
			}

			maVDev.SetLineColor(aOldLineColor);
			maVDev.SetFillColor(aOldFillColor);
		}
	}
}

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

void EMFWriter::ImplWrite( const GDIMetaFile& rMtf )
{
	for( sal_uLong j = 0, nActionCount = rMtf.GetActionCount(); j < nActionCount; j++ )
	{
		const MetaAction*	pAction = rMtf.GetAction( j );
		const sal_uInt16		nType = pAction->GetType();

		switch( nType )
		{
			case( META_PIXEL_ACTION	):
			{
				const MetaPixelAction* pA = (const MetaPixelAction*) pAction;

				ImplCheckLineAttr();
				ImplBeginRecord( WIN_EMR_SETPIXELV );
				ImplWritePoint( pA->GetPoint() );
				ImplWriteColor( pA->GetColor() );
				ImplEndRecord();
			}
			break;

			case( META_POINT_ACTION	):
			{
				if( maVDev.IsLineColor() )
				{
					const MetaPointAction* pA = (const MetaPointAction*) pAction;

					ImplCheckLineAttr();
					ImplBeginRecord( WIN_EMR_SETPIXELV );
					ImplWritePoint( pA->GetPoint() );
					ImplWriteColor( maVDev.GetLineColor() );
					ImplEndRecord();
				}
			}
			break;

			case( META_LINE_ACTION ):
			{
				if( maVDev.IsLineColor() )
				{
					const MetaLineAction* pA = (const MetaLineAction*) pAction;

                    if(pA->GetLineInfo().IsDefault())
                    {
					    ImplCheckLineAttr();

					    ImplBeginRecord( WIN_EMR_MOVETOEX );
					    ImplWritePoint( pA->GetStartPoint() );
					    ImplEndRecord();

					    ImplBeginRecord( WIN_EMR_LINETO );
					    ImplWritePoint( pA->GetEndPoint() );
					    ImplEndRecord();

					    ImplBeginRecord( WIN_EMR_SETPIXELV );
					    ImplWritePoint( pA->GetEndPoint() );
					    ImplWriteColor( maVDev.GetLineColor() );
					    ImplEndRecord();
                    }
                    else
                    {
                        // LineInfo used; handle Dash/Dot and fat lines
                        basegfx::B2DPolygon aPolygon;
                        aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
                        aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
                        Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
                    }
				}
			}
			break;

			case( META_RECT_ACTION ):
			{
				if( maVDev.IsLineColor() || maVDev.IsFillColor() )
				{
					const MetaRectAction* pA = (const MetaRectAction*) pAction;

					ImplCheckFillAttr();
					ImplCheckLineAttr();

					ImplBeginRecord( WIN_EMR_RECTANGLE );
					ImplWriteRect( pA->GetRect() );
					ImplEndRecord();
				}
			}
			break;

			case( META_ROUNDRECT_ACTION	):
			{
				if( maVDev.IsLineColor() || maVDev.IsFillColor() )
				{
					const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;

					ImplCheckFillAttr();
					ImplCheckLineAttr();

					ImplBeginRecord( WIN_EMR_ROUNDRECT );
					ImplWriteRect( pA->GetRect() );
					ImplWriteSize( Size( pA->GetHorzRound(), pA->GetVertRound() ) );
					ImplEndRecord();
				}
			}
			break;

			case( META_ELLIPSE_ACTION ):
			{
				if( maVDev.IsLineColor() || maVDev.IsFillColor() )
				{
					const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction;

					ImplCheckFillAttr();
					ImplCheckLineAttr();

					ImplBeginRecord( WIN_EMR_ELLIPSE );
					ImplWriteRect( pA->GetRect() );
					ImplEndRecord();
				}
			}
			break;

			case( META_ARC_ACTION ):
			case( META_PIE_ACTION ):
			case( META_CHORD_ACTION	):
			case( META_POLYGON_ACTION ):
			{
				if( maVDev.IsLineColor() || maVDev.IsFillColor() )
				{
					Polygon aPoly;

					switch( nType )
					{
						case( META_ARC_ACTION ):
						{
							const MetaArcAction* pA = (const MetaArcAction*) pAction;
							aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC );
						}
						break;

						case( META_PIE_ACTION ):
						{
							const MetaPieAction* pA = (const MetaPieAction*) pAction;
							aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE );
						}
						break;

						case( META_CHORD_ACTION	):
						{
							const MetaChordAction* pA = (const MetaChordAction*) pAction;
							aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD );
						}
						break;

						case( META_POLYGON_ACTION ):
							aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon();
						break;
					}

					ImplWritePolygonRecord( aPoly, nType != META_ARC_ACTION );
				}
			}
			break;

			case( META_POLYLINE_ACTION ):
			{
				if( maVDev.IsLineColor() )
                {
				    const MetaPolyLineAction*	pA = (const MetaPolyLineAction*) pAction;
				    const Polygon&				rPoly = pA->GetPolygon();

			        if( rPoly.GetSize() )
                    {
                        if(pA->GetLineInfo().IsDefault())
                        {
    					    ImplWritePolygonRecord( rPoly, sal_False );
                        }
                        else
                        {
                            // LineInfo used; handle Dash/Dot and fat lines
                            Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
                        }
                    }
                }
			}
			break;

			case( META_POLYPOLYGON_ACTION ):
			{
				if( maVDev.IsLineColor() || maVDev.IsFillColor() )
					ImplWritePolyPolygonRecord( ( (const MetaPolyPolygonAction*) pAction )->GetPolyPolygon() );
			}
			break;

			case( META_GRADIENT_ACTION ):
			{
				const MetaGradientAction*	pA = (const MetaGradientAction*) pAction;
				GDIMetaFile					aTmpMtf;

				maVDev.AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf );
				ImplWrite( aTmpMtf );
			}
			break;

			case META_HATCH_ACTION:
			{
				const MetaHatchAction*	pA = (const MetaHatchAction*) pAction;
				GDIMetaFile				aTmpMtf;

				maVDev.AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
				ImplWrite( aTmpMtf );
			}
			break;

			case META_TRANSPARENT_ACTION:
			{
				ImplCheckFillAttr();
				ImplCheckLineAttr();
				ImplWritePolyPolygonRecord( ( (MetaTransparentAction*) pAction )->GetPolyPolygon() );
			}
			break;

			case META_FLOATTRANSPARENT_ACTION:
			{
				const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction;

				GDIMetaFile		aTmpMtf( pA->GetGDIMetaFile() );
				Point			aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
				const Size		aSrcSize( aTmpMtf.GetPrefSize() );
				const Point		aDestPt( pA->GetPoint() );
				const Size		aDestSize( pA->GetSize() );
				const double	fScaleX = aSrcSize.Width() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
				const double	fScaleY = aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
				long			nMoveX, nMoveY;

				if( fScaleX != 1.0 || fScaleY != 1.0 )
				{
					aTmpMtf.Scale( fScaleX, fScaleY );
					aSrcPt.X() = FRound( aSrcPt.X() * fScaleX ), aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY );
				}

				nMoveX = aDestPt.X() - aSrcPt.X(), nMoveY = aDestPt.Y() - aSrcPt.Y();

				if( nMoveX || nMoveY )
					aTmpMtf.Move( nMoveX, nMoveY );

				ImplCheckFillAttr();
				ImplCheckLineAttr();
				ImplCheckTextAttr();
				ImplWrite( aTmpMtf );
			}
			break;

			case( META_EPS_ACTION ):
			{
				const MetaEPSAction*	pA = (const MetaEPSAction*) pAction;
				const GDIMetaFile		aSubstitute( pA->GetSubstitute() );

				for( sal_uLong i = 0, nCount = aSubstitute.GetActionCount(); i < nCount; i++ )
				{
					const MetaAction* pSubstAct = aSubstitute.GetAction( i );
					if( pSubstAct->GetType() == META_BMPSCALE_ACTION )
					{
						maVDev.Push( PUSH_ALL );
						ImplBeginRecord( WIN_EMR_SAVEDC );
						ImplEndRecord();

						MapMode	aMapMode( aSubstitute.GetPrefMapMode() );
						Size aOutSize( maVDev.LogicToLogic( pA->GetSize(), maVDev.GetMapMode(), aMapMode ) );
						aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
						aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
						aMapMode.SetOrigin( maVDev.LogicToLogic( pA->GetPoint(), maVDev.GetMapMode(), aMapMode ) );
						maVDev.SetMapMode( aMapMode );
						ImplWrite( aSubstitute );

						maVDev.Pop();
						ImplBeginRecord( WIN_EMR_RESTOREDC );
						(*mpStm) << (sal_Int32) -1;
						ImplEndRecord();
						break;
					}
				}
			}
			break;

			case META_BMP_ACTION:
			{
				const MetaBmpAction* pA = (const MetaBmpAction *) pAction;
				ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), maVDev.PixelToLogic( pA->GetBitmap().GetSizePixel() ), WIN_SRCCOPY );
			}
			break;

			case META_BMPSCALE_ACTION:
			{
				const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
				ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
			}
			break;

			case META_BMPSCALEPART_ACTION:
			{
				const MetaBmpScalePartAction*	pA = (const MetaBmpScalePartAction*) pAction;
				Bitmap							aTmp( pA->GetBitmap() );

				if( aTmp.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ) )
					ImplWriteBmpRecord( aTmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
			}
			break;

			case META_BMPEX_ACTION:
			{
				const MetaBmpExAction*	pA = (const MetaBmpExAction *) pAction;
				Bitmap					aBmp( pA->GetBitmapEx().GetBitmap() );
				Bitmap					aMsk( pA->GetBitmapEx().GetMask() );

				if( !!aMsk )
				{
					aBmp.Replace( aMsk, COL_WHITE );
					aMsk.Invert();
					ImplWriteBmpRecord( aMsk, pA->GetPoint(), maVDev.PixelToLogic( aMsk.GetSizePixel() ), WIN_SRCPAINT );
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), maVDev.PixelToLogic( aBmp.GetSizePixel() ), WIN_SRCAND );
				}
				else
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), aBmp.GetSizePixel(), WIN_SRCCOPY );
			}
			break;

			case META_BMPEXSCALE_ACTION:
			{
				const MetaBmpExScaleAction*	pA = (const MetaBmpExScaleAction*) pAction;
				Bitmap						aBmp( pA->GetBitmapEx().GetBitmap() );
				Bitmap						aMsk( pA->GetBitmapEx().GetMask() );

				if( !!aMsk )
				{
					aBmp.Replace( aMsk, COL_WHITE );
					aMsk.Invert();
					ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT );
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND );
				}
				else
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
			}
			break;

			case META_BMPEXSCALEPART_ACTION:
			{
				const MetaBmpExScalePartAction*	pA = (const MetaBmpExScalePartAction*) pAction;
				BitmapEx						aBmpEx( pA->GetBitmapEx() );
				aBmpEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
				Bitmap							aBmp( aBmpEx.GetBitmap() );
				Bitmap							aMsk( aBmpEx.GetMask() );

				if( !!aMsk )
				{
					aBmp.Replace( aMsk, COL_WHITE );
					aMsk.Invert();
					ImplWriteBmpRecord( aMsk, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCPAINT );
					ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCAND );
				}
				else
					ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
			}
			break;

			case META_TEXT_ACTION:
			{
				const MetaTextAction*	pA = (const MetaTextAction*) pAction;
				const String			aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

				ImplCheckTextAttr();
				ImplWriteTextRecord( pA->GetPoint(), aText, NULL, 0 );
			}
			break;

			case META_TEXTRECT_ACTION:
			{
				const MetaTextRectAction*	pA = (const MetaTextRectAction*) pAction;
				const String				aText( pA->GetText() );

				ImplCheckTextAttr();
				ImplWriteTextRecord( pA->GetRect().TopLeft(), aText, NULL, 0 );
			}
			break;

			case META_TEXTARRAY_ACTION:
			{
				const MetaTextArrayAction*	pA = (const MetaTextArrayAction*) pAction;
				const String				aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

				ImplCheckTextAttr();
				ImplWriteTextRecord( pA->GetPoint(), aText, pA->GetDXArray(), 0 );
			}
			break;

			case META_STRETCHTEXT_ACTION:
			{
				const MetaStretchTextAction*	pA = (const MetaStretchTextAction*) pAction;
				const String					aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

				ImplCheckTextAttr();
				ImplWriteTextRecord( pA->GetPoint(), aText, NULL, pA->GetWidth() );
			}
			break;

			case( META_LINECOLOR_ACTION	):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );
				mbLineChanged = sal_True;
			}
			break;

			case( META_FILLCOLOR_ACTION	):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );
				mbFillChanged = sal_True;
			}
			break;

			case( META_TEXTCOLOR_ACTION	):
			case( META_TEXTLINECOLOR_ACTION ):
			case( META_TEXTFILLCOLOR_ACTION	):
			case( META_TEXTALIGN_ACTION	):
			case( META_FONT_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );
				mbTextChanged = sal_True;
			}
			break;

			case( META_ISECTRECTCLIPREGION_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );

				ImplBeginRecord( WIN_EMR_INTERSECTCLIPRECT );
				ImplWriteRect( ( (MetaISectRectClipRegionAction*) pAction )->GetRect() );
				ImplEndRecord();
			}
			break;

			case( META_CLIPREGION_ACTION ):
			case( META_ISECTREGIONCLIPREGION_ACTION	):
			case( META_MOVECLIPREGION_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );
			}
			break;

			case( META_REFPOINT_ACTION ):
			case( META_MAPMODE_ACTION ):
				( (MetaAction*) pAction )->Execute( &maVDev );
			break;

			case( META_PUSH_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );

				ImplBeginRecord( WIN_EMR_SAVEDC );
				ImplEndRecord();
			}
			break;

			case( META_POP_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );

				ImplBeginRecord( WIN_EMR_RESTOREDC );
				(*mpStm) << (sal_Int32) -1;
				ImplEndRecord();

				ImplWriteRasterOp( maVDev.GetRasterOp() );
				mbLineChanged = mbFillChanged = mbTextChanged = sal_True;
			}
			break;

			case( META_RASTEROP_ACTION ):
			{
				( (MetaAction*) pAction )->Execute( &maVDev );
				ImplWriteRasterOp( ( (MetaRasterOpAction*) pAction )->GetRasterOp() );
			}
			break;

			case( META_LAYOUTMODE_ACTION ):
			{
				sal_uInt32 nLayoutMode = ( (MetaLayoutModeAction*) pAction )->GetLayoutMode();
				mnHorTextAlign = 0;
				if (nLayoutMode & TEXT_LAYOUT_BIDI_RTL)
				{
					mnHorTextAlign = TA_RIGHT | TA_RTLREADING;
				}
				if (nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT)
					mnHorTextAlign |= TA_RIGHT;
				else if (nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT)
					mnHorTextAlign &= ~TA_RIGHT;
				break;
			}

			case( META_MASK_ACTION ):
			case( META_MASKSCALE_ACTION	):
			case( META_MASKSCALEPART_ACTION	):
			case( META_WALLPAPER_ACTION	):
			case( META_TEXTLINE_ACTION ):
			case( META_COMMENT_ACTION ):
			case( META_GRADIENTEX_ACTION ):
			{
				// !!! >>> we don't want to support these actions
			}
			break;

			case( META_RENDERGRAPHIC_ACTION ):
			{
				const MetaRenderGraphicAction*	        pA = (const MetaRenderGraphicAction*) pAction;
				const ::vcl::RenderGraphicRasterizer    aRasterizer( pA->GetRenderGraphic() );
				const BitmapEx                          aBmpEx( aRasterizer.Rasterize( maVDev.LogicToPixel( pA->GetSize() ) ) );
				Bitmap						            aBmp( aBmpEx.GetBitmap() );
				Bitmap						            aMsk( aBmpEx.GetMask() );

				if( !!aMsk )
				{
					aBmp.Replace( aMsk, COL_WHITE );
					aMsk.Invert();
					ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT );
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND );
				}
				else
					ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
			}
			break;

			default:
				DBG_ERROR( ( ByteString( "EMFWriter::ImplWriteActions: unsupported MetaAction #" ) += ByteString::CreateFromInt32( nType ) ).GetBuffer() );
			break;
		}
	}
}
