Click here to Skip to main content
15,896,111 members
Articles / Multimedia / GDI+

Presenting EMFexplorer, a GDI+ experiment

Rate me:
Please Sign up or sign in to vote.
4.90/5 (28 votes)
29 Sep 20047 min read 129.1K   3.2K   49  
High quality EMF rendering, using GDI+
/*
*	This file is part of the EMFexplorer projet.
*	Copyright (C) 2004 Smith Charles.
*
*	This library is free software; you can redistribute it and/or
*	modify it under the terms of the GNU Lesser General Public
*	License as published by the Free Software Foundation; either
*	version 2.1 of the License, or (at your option) any later version.
*
*   This library 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 for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*
*	Extension: for commercial use, apply the Equity Public License, which
*	adds to the normal terms of the GLPL a condition of donation to the author.
*   If you are interested in support for this source code,
*   contact Smith Charles <smith.charles@free.fr> for more information.
*/


#include "stdafx.h"
#include "SCEMFgdiParser.h"
#include "SCEMFRasterizerDefs.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "SCEMFDefs.h"
#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCDCCloner.h)

// to allow token pasting at the beginig of line
#define SC_TKNPASTE(fake)

#define SC_RECORD_DECL0(recType) \
	SC_TKNPASTE(nothing)##recType *pRec = (##recType*)m_pRecord; \
	if (pRec->emr.nSize < sizeof(##recType)) \
		return SC_BRK_NOERROR

#define SC_RECORD_DECL_PLAY(recType) \
	SC_TKNPASTE(nothing)##recType *pRec = (##recType*)m_pRecord; \
	if (pRec->emr.nSize < sizeof(##recType)) \
	{ \
		SCPlayRecord(); \
		return SC_BRK_NOERROR; \
	}

// Reminder
#ifndef SC_RENDERER_CAN_DOROP
// Warning: when m_hPlayDC is not a clone, calling functions like SetMapmode on it may
// perturbate GDI+. This was observed for printer DC.
#pragma message(__FILE__ "(47): SC_RENDERER_CAN_DOROP not defined. Potential printing problems.")
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSCEMFgdiParser::CSCEMFgdiParser():
	SCBrkEMF(),
	m_pRenderer(NULL),
	m_hDestDC(NULL)
#ifdef SC_USE_DCCLONER
	,m_pCloneDC(NULL)
#endif
{
	memset(&m_xformOrig, 0, sizeof(m_xformOrig));
	m_xformOrig.eM11 = m_xformOrig.eM22 = 1;
}

CSCEMFgdiParser::CSCEMFgdiParser(I_EMFRenderer* pRenderer):
	SCBrkEMF(),
	m_pRenderer(pRenderer),
	m_hDestDC(NULL)
#ifdef SC_USE_DCCLONER
	,m_pCloneDC(NULL)
#endif
{
	memset(&m_xformOrig, 0, sizeof(m_xformOrig));
	m_xformOrig.eM11 = m_xformOrig.eM22 = 1;
}


CSCEMFgdiParser::~CSCEMFgdiParser()
{
#ifdef SC_USE_DCCLONER
	// delete temporary DC
	if (m_pCloneDC)
		delete m_pCloneDC;
#endif
}

void CSCEMFgdiParser::SCResetCracker()
{
}

//////////////////////////////////////////////////////////////////////
// Events methods
//////////////////////////////////////////////////////////////////////
SC_BRKRESULT CSCEMFgdiParser::OnEmfHEADER()
{
	// TRACE0("**EMR_HEADER\n");
	ASSERT(m_pRenderer);

	//ENHMETAHEADER *pRec = (ENHMETAHEADER*)m_pRecord;

	SCPlayRecord();
	m_pRenderer->SCBeginRendering(m_hDestDC, m_hPlayDC);

	return SCBrkEMF::OnEmfHEADER();
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIER()
{
	// TRACE0("**EMR_POLYBEZIER\n");
	ASSERT(m_pRenderer);

	EMRPOLYBEZIER *pRec = (EMRPOLYBEZIER*)m_pRecord;
	m_pRenderer->SCDrawBezier((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYGON()
{
	// TRACE0("**EMR_POLYGON\n");
	ASSERT(m_pRenderer);

	EMRPOLYGON *pRec = (EMRPOLYGON*)m_pRecord;
	m_pRenderer->SCDrawPolygon((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINE()
{
	// TRACE0("**EMR_POLYLINE\n");
	ASSERT(m_pRenderer);

	EMRPOLYLINE *pRec = (EMRPOLYLINE*)m_pRecord;
	m_pRenderer->SCDrawLines((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIERTO()
{
	// TRACE0("**EMR_POLYBEZIERTO\n");
	ASSERT(m_pRenderer);

	EMRPOLYBEZIERTO *pRec = (EMRPOLYBEZIERTO*)m_pRecord;
	m_pRenderer->SCDrawBezierTo((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINETO()
{
	// TRACE0("**EMR_POLYLINETO\n");
	ASSERT(m_pRenderer);

	EMRPOLYLINETO *pRec = (EMRPOLYLINETO*)m_pRecord;
	m_pRenderer->SCDrawLinesTo((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYLINE()
{
	// TRACE0("**EMR_POLYPOLYLINE\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYLINE *pRec = (EMRPOLYPOLYLINE*)m_pRecord;
	LPPOINT pPts = (LPPOINT)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolyline(pPts, pRec->cptl, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYGON()
{
	// TRACE0("**EMR_POLYPOLYGON\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYGON *pRec = (EMRPOLYPOLYGON*)m_pRecord;
	LPPOINT pPts = (LPPOINT)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolygon(pPts, pRec->cptl, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWINDOWEXTEX()
{
	// TRACE0("**EMR_SETWINDOWEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWindowExtent(((EMRSETWINDOWEXTEX*)m_pRecord)->szlExtent.cx,
									((EMRSETWINDOWEXTEX*)m_pRecord)->szlExtent.cy);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWINDOWORGEX()
{
	// TRACE0("**EMR_SETWINDOWORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWindowOrg(((EMRSETWINDOWORGEX*)m_pRecord)->ptlOrigin.x,
								((EMRSETWINDOWORGEX*)m_pRecord)->ptlOrigin.y);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETVIEWPORTEXTEX()
{
	// TRACE0("**EMR_SETVIEWPORTEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetViewportExtent(((EMRSETVIEWPORTEXTEX*)m_pRecord)->szlExtent.cx,
									 ((EMRSETVIEWPORTEXTEX*)m_pRecord)->szlExtent.cy);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETVIEWPORTORGEX()
{
	// TRACE0("**EMR_SETVIEWPORTORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetViewportOrg(((EMRSETVIEWPORTORGEX*)m_pRecord)->ptlOrigin.x,
								  ((EMRSETVIEWPORTORGEX*)m_pRecord)->ptlOrigin.y);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBRUSHORGEX()
{
	// TRACE0("**EMR_SETBRUSHORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management (via stretchbltmode and halftone)
#endif

	m_pRenderer->SCSetBrushOrg(((EMRSETBRUSHORGEX*)m_pRecord)->ptlOrigin);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEOF()
{
	// TRACE0("**EMR_EOF\n");
	ASSERT(m_pRenderer);

	// (EMREOF*)m_pRecord;
	SCPlayRecord();
	m_pRenderer->SCEndRendering();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPIXELV()
{
	// TRACE0("**EMR_SETPIXELV\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPixel(((EMRSETPIXELV*)m_pRecord)->ptlPixel,
							 ((EMRSETPIXELV*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMAPPERFLAGS()
{
	// TRACE0("**EMR_SETMAPPERFLAGS\n");
	ASSERT(m_pRenderer);

	// (EMRSETMAPPERFLAGS*)m_pRecord;

	// No aspect ratio management, as we only use TT fonts.
	// Anyway let Windows do it.
	SCPlayRecord();		
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMAPMODE()
{
	// TRACE0("**EMR_SETMAPMODE\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management (via viewport/window settings)
#endif

	m_pRenderer->SCSetMapMode(((EMRSETMAPMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBKMODE()
{
	// TRACE0("**EMR_SETBKMODE\n");
	ASSERT(m_pRenderer);
	//SCPlayRecord(); // wait and see

	m_pRenderer->SCSetBkMode(((EMRSETBKMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPOLYFILLMODE()
{
	// TRACE0("**EMR_SETPOLYFILLMODE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetPolyFillMode(((EMRSETPOLYFILLMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETROP2()
{
	// TRACE0("**EMR_SETROP2\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetROP2(((EMRSETROP2*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETSTRETCHBLTMODE()
{
	// TRACE0("**EMR_SETSTRETCHBLTMODE\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetStretchBltMode(((EMRSETSTRETCHBLTMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTALIGN()
{
	// TRACE0("**EMR_SETTEXTALIGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetTextAlign(((EMRSETTEXTALIGN*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETCOLORADJUSTMENT()
{
	// TRACE0("**EMR_SETCOLORADJUSTMENT\n");
	ASSERT(m_pRenderer);


#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // for ROP (just in case)
#endif
	// "The color adjustment values are used to adjust the input color of the source bitmap
	// for calls to the StretchBlt and StretchDIBits functions when HALFTONE mode is set."
	// But it's NT specific.

	m_pRenderer->SCSetColorAsjustment(&((EMRSETCOLORADJUSTMENT*)m_pRecord)->ColorAdjustment);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTCOLOR()
{
	// TRACE0("**EMR_SETTEXTCOLOR\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetTextColor(((EMRSETTEXTCOLOR*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBKCOLOR()
{
	// TRACE0("**EMR_SETBKCOLOR\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetBkColor(((EMRSETBKCOLOR*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfOFFSETCLIPRGN()
{
	// TRACE0("**EMR_OFFSETCLIPRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCOffsetClipRect(((EMROFFSETCLIPRGN*)m_pRecord)->ptlOffset);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMOVETOEX()
{
	// TRACE0("**EMR_MOVETOEX\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCMoveToEx(((EMRMOVETOEX*)m_pRecord)->ptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMETARGN()
{
	// TRACE0("**EMR_SETMETARGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCOnSetMetaRgn();
	
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXCLUDECLIPRECT()
{
	// TRACE0("**EMR_EXCLUDECLIPRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCExcludeClipRect(((EMREXCLUDECLIPRECT*)m_pRecord)->rclClip);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfINTERSECTCLIPRECT()
{
	// TRACE0("**EMR_INTERSECTCLIPRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCIntersectClipRect(((EMRINTERSECTCLIPRECT*)m_pRecord)->rclClip);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSCALEVIEWPORTEXTEX()
{
	// TRACE0("**EMR_SCALEVIEWPORTEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	EMRSCALEVIEWPORTEXTEX *pRec = (EMRSCALEVIEWPORTEXTEX*)m_pRecord;
	m_pRenderer->SCScaleViewport(pRec->xNum, pRec->xDenom, pRec->yNum, pRec->yDenom);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSCALEWINDOWEXTEX()
{
	// TRACE0("**EMR_SCALEWINDOWEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	EMRSCALEWINDOWEXTEX *pRec = (EMRSCALEWINDOWEXTEX*)m_pRecord;
	m_pRenderer->SCScaleWindow(pRec->xNum, pRec->xDenom, pRec->yNum, pRec->yDenom);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSAVEDC()
{
	// TRACE0("**EMR_SAVEDC\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	m_pRenderer->SCOnDCSaved();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRESTOREDC()
{
	// TRACE0("**EMR_RESTOREDC\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();
	
	// Be careful : sometimes we get bad values from the record.
	// Some documents contain values like 30,
	// though there is only one DC state on the stack
	m_pRenderer->SCOnDCRestored(((EMRRESTOREDC*)m_pRecord)->iRelative);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWORLDTRANSFORM()
{
	// TRACE0("**EMR_SETWORLDTRANSFORM\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWorldTransform(((EMRSETWORLDTRANSFORM*)m_pRecord)->xform);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMODIFYWORLDTRANSFORM()
{
	// TRACE0("**EMR_MODIFYWORLDTRANSFORM\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCModifyWorldTransform(((EMRMODIFYWORLDTRANSFORM*)m_pRecord)->xform,
										((EMRMODIFYWORLDTRANSFORM*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTOBJECT()
{
	// TRACE0("**EMR_SELECTOBJECT\n");
	ASSERT(m_pRenderer);

	// First, play the record to realize GDI objects
	SCPlayRecord();

	// Then extract objects characteristics
	EMRSELECTOBJECT *pRec = (EMRSELECTOBJECT*)m_pRecord;
	HGDIOBJ hGdiObj;
	// Is the high order bit set?
	if (pRec->ihObject & 0x80000000)
	{
		// High order bit is set - its a stock object
		// Strip the high bit to get the index
		DWORD dwIndex = pRec->ihObject & 0x7fffffff;
		// Pass the index to GetStockObject()
		hGdiObj = GetStockObject( dwIndex );
	} else
		// High order bit isn't set - not a stock object
		hGdiObj = (HGDIOBJ)m_lpEnumHandleTable->objectHandle[pRec->ihObject];

	long lType = GetObjectType(hGdiObj);
	switch(lType)
	{
		case OBJ_EXTPEN:
			m_pRenderer->SCOnChangeExtPen((HPEN)hGdiObj);
			break;

		case OBJ_PEN:
			m_pRenderer->SCOnChangePen((HPEN)hGdiObj);
			break;

		case OBJ_BRUSH:
			m_pRenderer->SCOnChangeBrush((HBRUSH)hGdiObj);
			break;
			
		case OBJ_FONT:
			m_pRenderer->SCOnChangeFont((HFONT)hGdiObj);
			break;
			
		//case OBJ_PAL:
		// unlikely to occur (The SelectObject function does not work with palettes)
		//m_pRenderer->SCOnChangePalette((HPALETTE)hGdiObj);
		//break;								
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEPEN()
{
	// TRACE0("**EMR_CREATEPEN\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEPEN *pRec = (EMRCREATEPEN*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEBRUSHINDIRECT()
{
	// TRACE0("**EMR_CREATEBRUSHINDIRECT\n");
	ASSERT(m_pRenderer);

	// Note: due to dithering, Windows may change BS_SOLID in BS_PATTERN
	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEBRUSHINDIRECT *pRec = (EMRCREATEBRUSHINDIRECT*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDELETEOBJECT()
{
	// TRACE0("**EMR_DELETEOBJECT\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object deletion: do nothing
	// EMRDELETEOBJECT *pRec = (EMRDELETEOBJECT*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfANGLEARC()
{
	// TRACE0("**EMR_ANGLEARC\n");
	ASSERT(m_pRenderer);

	EMRANGLEARC *pRec = (EMRANGLEARC*)m_pRecord;
	m_pRenderer->SCDrawAngleArc((LPPOINT)&pRec->ptlCenter, pRec->nRadius, pRec->eStartAngle, pRec->eSweepAngle);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfELLIPSE()
{
	// TRACE0("**EMR_ELLIPSE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawEllipse((LPCRECT)&((EMRELLIPSE*)m_pRecord)->rclBox);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRECTANGLE()
{
	// TRACE0("**EMR_RECTANGLE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawRectangle((LPCRECT)&((EMRRECTANGLE*)m_pRecord)->rclBox);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfROUNDRECT()
{
	// TRACE0("**EMR_ROUNDRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawRoundRectangle((LPCRECT)&((EMRROUNDRECT*)m_pRecord)->rclBox,
												((EMRROUNDRECT*)m_pRecord)->szlCorner);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfARC()
{
	// TRACE0("**EMR_ARC\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawArc(&((EMRARC*)m_pRecord)->rclBox,
						   &((EMRARC*)m_pRecord)->ptlStart,
						   &((EMRARC*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCHORD()
{
	// TRACE0("**EMR_CHORD\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawChord(&((EMRCHORD*)m_pRecord)->rclBox,
							 &((EMRCHORD*)m_pRecord)->ptlStart,
							 &((EMRCHORD*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPIE()
{
	// TRACE0("**EMR_PIE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPie(&((EMRPIE*)m_pRecord)->rclBox,
						   &((EMRPIE*)m_pRecord)->ptlStart,
						   &((EMRPIE*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;

}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTPALETTE()
{
	// TRACE0("**EMR_SELECTPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	m_pRenderer->SCOnChangePalette((HPALETTE)((EMRSELECTPALETTE*)m_pRecord)->ihPal);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEPALETTE()
{
	// TRACE0("**EMR_CREATEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEPALETTE *pRec = (EMRCREATEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPALETTEENTRIES()
{
	// TRACE0("**EMR_SETPALETTEENTRIES\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRSETPALETTEENTRIES *pRec = (EMRSETPALETTEENTRIES*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRESIZEPALETTE()
{
	// TRACE0("**EMR_RESIZEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRRESIZEPALETTE *pRec = (EMRRESIZEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfREALIZEPALETTE()
{
	// TRACE0("**EMR_REALIZEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRREALIZEPALETTE *pRec = (EMRREALIZEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTFLOODFILL()
{
	// TRACE0("**EMR_EXTFLOODFILL\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFloodFill(((EMREXTFLOODFILL*)m_pRecord)->ptlStart,
							 ((EMREXTFLOODFILL*)m_pRecord)->crColor,
							 ((EMREXTFLOODFILL*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfLINETO()
{
	// TRACE0("**EMR_LINETO\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesTo((LPPOINT)&((EMRLINETO*)m_pRecord)->ptl, 1);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfARCTO()
{
	// TRACE0("**EMR_ARCTO\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawArcTo(&((EMRARCTO*)m_pRecord)->rclBox,
							 &((EMRARCTO*)m_pRecord)->ptlStart,
							 &((EMRARCTO*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYDRAW()
{
	// TRACE0("**EMR_POLYDRAW\n");
	ASSERT(m_pRenderer);

	EMRPOLYDRAW *pRec = (EMRPOLYDRAW*)m_pRecord;

	BYTE* pTypes = (BYTE*)((POINTL*)pRec->aptl + pRec->cptl);
	m_pRenderer->SCDrawPolyDraw((POINT*)pRec->aptl,
								pRec->cptl,
								pTypes);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETARCDIRECTION()
{
	// TRACE0("**EMR_SETARCDIRECTION\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetArcDirection(((EMRSETARCDIRECTION*)m_pRecord)->iArcDirection);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMITERLIMIT()
{
	// TRACE0("**EMR_SETMITERLIMIT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetMiterLimit(((EMRSETMITERLIMIT*)m_pRecord)->eMiterLimit);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfBEGINPATH()
{
	// TRACE0("**EMR_BEGINPATH\n");
	ASSERT(m_pRenderer);

	//EMRBEGINPATH *pRec = (EMRBEGINPATH*)m_pRecord;

	m_pRenderer->SCBeginPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfENDPATH()
{
	// TRACE0("**EMR_ENDPATH\n");
	ASSERT(m_pRenderer);

	//EMRENDPATH *pRec = (EMRENDPATH*)m_pRecord;

	m_pRenderer->SCEndPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCLOSEFIGURE()
{
	// TRACE0("**EMR_CLOSEFIGURE\n");
	ASSERT(m_pRenderer);

	//EMRCLOSEFIGURE *pRec = (EMRCLOSEFIGURE*)m_pRecord;

	m_pRenderer->SCCloseFigure();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFILLPATH()
{
	// TRACE0("**EMR_FILLPATH\n");
	ASSERT(m_pRenderer);

	//EMRFILLPATH *pRec = (EMRFILLPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_FILL);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTROKEANDFILLPATH()
{
	// TRACE0("**EMR_STROKEANDFILLPATH\n");
	ASSERT(m_pRenderer);

	//EMRSTROKEANDFILLPATH *pRec = (EMRSTROKEANDFILLPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_STROKEANDFILL);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTROKEPATH()
{
	// TRACE0("**EMR_STROKEPATH\n");
	ASSERT(m_pRenderer);

	//EMRSTROKEPATH *pRec = (EMRSTROKEPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_STROKE);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFLATTENPATH()
{
	// TRACE0("**EMR_FLATTENPATH\n");
	ASSERT(m_pRenderer);

	//EMRFLATTENPATH *pRec = (EMRFLATTENPATH*)m_pRecord;

	m_pRenderer->SCFlattenPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfWIDENPATH()
{
	// TRACE0("**EMR_WIDENPATH\n");
	ASSERT(m_pRenderer);

	//EMRWIDENPATH *pRec = (EMRWIDENPATH*)m_pRecord;

	m_pRenderer->SCWidenPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTCLIPPATH()
{
	// TRACE0("**EMR_SELECTCLIPPATH\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCApplyPath(SC_PATH_CLIP, ((EMRSELECTCLIPPATH*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfABORTPATH()
{
	// TRACE0("**EMR_ABORTPATH\n");
	ASSERT(m_pRenderer);

	//EMRABORTPATH *pRec = (EMRABORTPATH*)m_pRecord;

	m_pRenderer->SCAbortPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGDICOMMENT()
{
	// TRACE0("**EMR_GDICOMMENT\n");
	ASSERT(m_pRenderer);

#pragma message( __FILE__  "(894): TODO: Recurse parser on embedded WMF? ")
#if 0
	EMRGDICOMMENT *pRec = (EMRGDICOMMENT*)m_pRecord;

	DWORD* pDW = (DWORD*)pRec->Data;
	if (GDICOMMENT_IDENTIFIER==*pDW)
	{
		pDW++;
		switch(*pDW)
		{
		case GDICOMMENT_WINDOWS_METAFILE:
			// embedded WMF
				// TODO:
			break;
			
		case GDICOMMENT_MULTIFORMATS:
			// embedded formats
				// TODO:
			break;
		}
	}
#endif

	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFILLRGN()
{
	// TRACE0("**EMR_FILLRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFillRgn((HBRUSH)m_lpEnumHandleTable->objectHandle[((EMRFILLRGN*)m_pRecord)->ihBrush],
						   ((EMRFILLRGN*)m_pRecord)->RgnData,
						   ((EMRFILLRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFRAMERGN()
{
	// TRACE0("**EMR_FRAMERGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFrameRgn((HBRUSH)m_lpEnumHandleTable->objectHandle[((EMRFRAMERGN*)m_pRecord)->ihBrush],
							((EMRFRAMERGN*)m_pRecord)->szlStroke,
							((EMRFRAMERGN*)m_pRecord)->RgnData,
							((EMRFRAMERGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfINVERTRGN()
{
	// TRACE0("**EMR_INVERTRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCInvertRgn(((EMRINVERTRGN*)m_pRecord)->RgnData,
							 ((EMRINVERTRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPAINTRGN()
{
	// TRACE0("**EMR_PAINTRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCPaintRgn(((EMRPAINTRGN*)m_pRecord)->RgnData,
							((EMRPAINTRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTSELECTCLIPRGN()
{
	// TRACE0("**EMR_EXTSELECTCLIPRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSelectClipRgn(((EMREXTSELECTCLIPRGN*)m_pRecord)->RgnData,
								 ((EMREXTSELECTCLIPRGN*)m_pRecord)->cbRgnData,
								 ((EMREXTSELECTCLIPRGN*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfBITBLT()
{
	// TRACE0("**EMR_BITBLT\n");
	ASSERT(m_pRenderer);

	EMRBITBLT *pRec = (EMRBITBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source and Destination have same size for BitBlt function
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxDest, pRec->ySrc + pRec->cyDest);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTRETCHBLT()
{
	// TRACE0("**EMR_STRETCHBLT\n");
	ASSERT(m_pRenderer);

	EMRSTRETCHBLT *pRec = (EMRSTRETCHBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMASKBLT()
{
	// TRACE0("**EMR_MASKBLT\n");
	ASSERT(m_pRenderer);

	EMRMASKBLT *pRec = (EMRMASKBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// the source bitmap
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// the mask bitmap is optional
		BITMAPINFO *pBmiMask = NULL;
		BYTE *pBitsMask = NULL;
		if (pRec->offBmiMask && pRec->offBitsMask)
		{
			pBmiMask = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiMask); 
			pBitsMask = (BYTE *)((BYTE *)pRec + pRec->offBitsMask);
		}

		// Source equals dest in dimensions
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxDest, pRec->ySrc + pRec->cyDest);

		m_pRenderer->SCDrawImageMsk(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc,
		pRec->crBkColorSrc, pRec->xMask, pRec->yMask, pBitsMask, pBmiMask, pRec->iUsageMask);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPLGBLT()
{
	// TRACE0("**EMR_PLGBLT\n");
	ASSERT(m_pRenderer);

	EMRPLGBLT *pRec = (EMRPLGBLT*)m_pRecord;

	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// the source bitmap
		BITMAPINFO *pBmi = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits = (BYTE *)((BYTE *)pRec + pRec->offBitsSrc);

		// the mask bitmap is optional
		BITMAPINFO *pBmiMask = NULL;
		BYTE *pBitsMask = NULL;
		if (pRec->offBmiMask && pRec->offBitsMask)
		{
			pBmiMask = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiMask); 
			pBitsMask = (BYTE *)((BYTE *)pRec + pRec->offBitsMask);
		}

		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);

		m_pRenderer->SCDrawImagePlg(pRec->aptlDest,
		&RcSrc, pBits, pBmi, pRec->iUsageSrc, &pRec->xformSrc, pRec->crBkColorSrc,
		pRec->xMask, pRec->yMask, pBitsMask, pBmiMask, pRec->iUsageMask);
		
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETDIBITSTODEVICE()
{
	// TRACE0("**EMR_SETDIBITSTODEVICE\n");
	ASSERT(m_pRenderer);

	EMRSETDIBITSTODEVICE *pRec = (EMRSETDIBITSTODEVICE*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxSrc, pRec->yDest + pRec->cySrc);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, SRCCOPY);
	} else
		m_pRenderer->SCPatBlt(&RcDest, SRCCOPY);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTRETCHDIBITS()
{
	ASSERT(m_pRenderer);

	// TRACE0("**EMR_STRETCHDIBITS\n");
	EMRSTRETCHDIBITS *pRec = (EMRSTRETCHDIBITS*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTCREATEFONTINDIRECTW()
{
	// TRACE0("**EMR_EXTCREATEFONTINDIRECTW\n");
	ASSERT(m_pRenderer);

	// Play the record on the DC, in order to get information about the physical font,
	// which is the font really used by the DC.
#if 0
	// Raster and vector fonts should be replaced by substitutes.

	// Example of early font substitution.
	// This is cleaner than late substitution (SCTTFontFromLOGFONT in SCGdiplusUtils.cpp);
	// but it penalizes good TT fonts
	// (memory allocations and string comparisons)
	EMREXTCREATEFONTINDIRECTW *pSubs = (EMREXTCREATEFONTINDIRECTW *)SCCloneRecord();
	if (pSubs)
	{
		LOGFONTW& rLogFont = pSubs->elfw.elfLogFont;
		DWORD dwFamily = SCFontFamilyApproximantW((WCHAR*)rLogFont.lfFaceName);
		if (FF_DONTCARE!=dwFamily)
		{
			rLogFont.lfPitchAndFamily |= dwFamily;
			rLogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		}
		
		SCPlayRecordAndFree(pSubs);
	} else
		SCPlayRecord();
#else
	SCPlayRecord();
#endif
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTTEXTOUTA()
{
	// TRACE0("**EMR_EXTTEXTOUTA or **EMR_EXTTEXTOUTW\n");
	ASSERT(m_pRenderer);

	SC_BRKRESULT error(SC_BRK_NOERROR);

	// the structure EMREXTTEXTOUTA is same as EMREXTTEXTOUTW
	EMREXTTEXTOUTA *pRec = (EMREXTTEXTOUTA*)m_pRecord;

	// get the EMRTEXT
	EMRTEXT *pTextRec = &(pRec->emrtext);

	// number of characters	
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
	{
		// We don't know the behavior of ExtTextOut:
		// - if number of characters is 0
		// - if, moreover, TA_UPDATECP is set in the DC
#if 0
		// if required, play the record
		SCPlayRecord();
#endif
		return 0;
	}

	UINT uiOptions = pTextRec->fOptions; // we might have to alter it

	// Build the string
	LPWSTR pwStr = NULL;
	if (pRec->emr.iType == EMR_EXTTEXTOUTW) 
	{
		// The string is in unicode -> we let it as is.
		// Offset is from the beginning of the struct and is in bytes.
		// 	
		// Get the unicode string
		pwStr = (LPWSTR) ((BYTE *)pRec + pTextRec->offString);

#if 0
		// TODO:
		// Translate glyph indices to code points when the rasterizer does not
		// support string measurement for glyph indices.
		if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
		{
			int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pString, iNbChars);
			if (iRes)
				uiOptions &= ~ETO_GLYPH_INDEX;
		}
#endif
	} else
	{
		// The string is single-byte encoded.
		// Get adress of the string	then construct the string.			
		LPSTR pString = (char *)pRec + pTextRec->offString;
		pwStr = new wchar_t[iNbChars+1];
		if (pwStr)
		{// We will use UNICODE strings exclusively
			pwStr[iNbChars] = 0;
			int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pString, iNbChars, pwStr, iNbChars+1);
			ASSERT(iNum==iNbChars);
		}
	}
	ASSERT(pwStr);

	// Get intercharacter spacing array.
	// Be careful ! there are cases where OffDx==0.
	DWORD OffDx = pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	if (0==OffDx)
	{
		// If the lpDx parameter is NULL, the ExtTextOut function uses the default
		// spacing between characters. So we do.
		//
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	}
	else
	{ // else use distances between origins of adjacent character cells as passed to ExtTextOut.
		// except that, sometimes, the last character is given a spacing of 0, which is
		// not good for us if this character has a non-null width.
		if (0==lpDx[iNbChars-1])
		{
			INT* pInts = new INT[iNbChars];
			memmove(pInts, lpDx, iNbChars*sizeof(INT));

			// compute only the last character's width
			BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr+iNbChars-1, 1, pInts+iNbChars-1);
			ASSERT(bOk);
			lpDx = pInts;
			OffDx = 0; // for cleanup
			//at this point, 0 is accepted
			//ASSERT(lpDx[iNbChars-1]);
		}
	}

#if 0
	// This thing is not accurate. We prefer to let the rasterizer use its computed rectangle
	// when no opaquing rectangle is supplied
	CRect rcText = (LPCRECT)&pTextRec->rcl;
	if (rcText.IsRectEmpty())
	{
		rcText.CopyRect((LPCRECT)&pRec->rclBounds);
		DPtoLP(m_hPlayDC, (LPPOINT)&rcText, 2);
	}
	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&rcText, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&rcText, pwStr, iNbChars, lpDx, 1, 1);
#else
	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, 1, 1);
#endif

	// Clean up
	if (pRec->emr.iType == EMR_EXTTEXTOUTA) 
		delete [] pwStr;
	if (0==OffDx)
		delete [] lpDx;

	return error;
}

//////////////////////////////////////////////////////////////////////////////////////
// It is unlikely that SCEmfTextoutA/SCEmfTextoutW are called. So I prefer to let
// them separated (with duplicated code). See OnEmfTextoutA for documentation
SC_BRKRESULT CSCEMFgdiParser::SCEmfTextoutA(EMREXTTEXTOUTA* pRec, EMRTEXT* pTextRec)
{
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
		return SC_BRK_NOERROR;

	// Get adress of the string	then construct the widestring.			
	LPWSTR pwStr = new wchar_t[iNbChars + 1];
	if (pwStr)
	{// We will use UNICODE strings exclusively
		pwStr[iNbChars] = 0;
		int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)((char *)pRec + pTextRec->offString),
			iNbChars, pwStr, iNbChars + 1);
		ASSERT(iNum==iNbChars);
		
		SCFinishTextout(pRec, pTextRec, pwStr, pTextRec->fOptions);
		
		// Clean up
		delete [] pwStr;
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::SCEmfTextoutW(EMREXTTEXTOUTW* pRec, EMRTEXT* pTextRec)
{
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
		return SC_BRK_NOERROR;

	UINT uiOptions = pTextRec->fOptions; // we might have to alter it

	// Get the unicode string
	LPWSTR pwStr = (LPWSTR) ((BYTE *)pRec + pTextRec->offString);

#if 0
	ASSERT(m_pRenderer);

	// TODO:
	// Translate glyph indices to code points when the rasterizer does not
	// support string measurement for glyph indices.
	if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
	{
		int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pwStr, iNbChars);
		if (iRes)
			uiOptions &= ~ETO_GLYPH_INDEX;
	}
#endif

	SCFinishTextout(pRec, pTextRec, pwStr, uiOptions);
	return SC_BRK_NOERROR;
}

void CSCEMFgdiParser::SCFinishTextout(EMREXTTEXTOUTA* pRec, EMRTEXT* pTextRec, LPWSTR pwStr, UINT uiOptions)
{
	ASSERT(m_pRenderer);

	INT iNbChars = pTextRec->nChars;
	// Get intercharacter spacing array.
	DWORD OffDx = pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	if (0==OffDx)
	{
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	}
	else
	{ // else use distances between origins of adjacent character cells as passed to ExtTextOut.
		// except that, sometimes, the last character is given a spacing of 0, which is not good for us.
		if (0==lpDx[iNbChars-1])
		{
			INT* pInts = new INT[iNbChars];
			memmove(pInts, lpDx, iNbChars*sizeof(INT));

			// compute only the last character's width
			BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr+iNbChars-1, 1, pInts+iNbChars-1);
			ASSERT(bOk);
			lpDx = pInts;
			OffDx = 0; // for cleanup
			ASSERT(lpDx[iNbChars-1]);
		}
	}

	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, 1, 1);

	// Clean up
	if (0==OffDx)
		delete [] lpDx;
}
///////////////////////////////////////////////////////////////////////////////////////////

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTTEXTOUTW()
{
	// TRACE0("**EMR_EXTTEXTOUTW\n");
	//EMREXTTEXTOUTW *pRec = (EMREXTTEXTOUTW*)m_pRecord;

	// pass the record to 
	return OnEmfEXTTEXTOUTA();
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIER16()
{
	// TRACE0("**EMR_POLYBEZIER16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawBezierS(((EMRPOLYBEZIER16*)m_pRecord)->apts,
							  ((EMRPOLYBEZIER16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYGON16()
{
	// TRACE0("**EMR_POLYGON16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPolygonS(((EMRPOLYGON16*)m_pRecord)->apts,
								((EMRPOLYGON16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINE16()
{
	// TRACE0("**EMR_POLYLINE16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesS(((EMRPOLYLINE16*)m_pRecord)->apts,
							 ((EMRPOLYLINE16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIERTO16()
{
	// TRACE0("**EMR_POLYBEZIERTO16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawBezierToS(((EMRPOLYBEZIERTO16*)m_pRecord)->apts,
								((EMRPOLYBEZIERTO16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINETO16()
{
	// TRACE0("**EMR_POLYLINETO16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesToS(((EMRPOLYLINETO16*)m_pRecord)->apts,
								((EMRPOLYLINETO16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYLINE16()
{
	// TRACE0("**EMR_POLYPOLYLINE16\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYLINE16 *pRec = (EMRPOLYPOLYLINE16*)m_pRecord;
	LPPOINTS pPts = (LPPOINTS)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolylineS(pPts, pRec->cpts, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYGON16()
{
	// TRACE0("**EMR_POLYPOLYGON16\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYGON16 *pRec = (EMRPOLYPOLYGON16*)m_pRecord;
	LPPOINTS pPts = (LPPOINTS)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolygonS(pPts, pRec->cpts, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYDRAW16()
{
	// TRACE0("**EMR_POLYDRAW16\n");
	ASSERT(m_pRenderer);

	EMRPOLYDRAW16 *pRec = (EMRPOLYDRAW16*)m_pRecord;

	BYTE* pTypes = (BYTE*)((POINTS*)pRec->apts + pRec->cpts);
	m_pRenderer->SCDrawPolyDrawS(pRec->apts,
								pRec->cpts,
								pTypes);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEMONOBRUSH()
{
	// TRACE0("**EMR_CREATEMONOBRUSH\n");
	ASSERT(m_pRenderer);

	EMRCREATEMONOBRUSH *pRec = (EMRCREATEMONOBRUSH*)m_pRecord;

	// SCPlayRecord() won't work: the brush will reference a deleted bitmap (XP specific bug?).
	// See comments in the final block.
	SCPlayRecord();

	#ifndef DIB_PAL_INDICES
		#define DIB_PAL_INDICES	2
	#endif
	ASSERT(DIB_PAL_INDICES==pRec->iUsage);

	// DIB_PAL_INDICES management. Special case: the DIB has no color table.
	BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmi); 
	DWORD *pBitsDW     = (DWORD *) ((BYTE *) pRec + pRec->offBits);
	// Attach a monochrome palette to the DIB specification,
	// and create a device-dependent bitmap
	HBITMAP hBm = NULL;
	DWORD dwSize = pBmi->bmiHeader.biSize + 2*sizeof(RGBQUAD);
	BITMAPINFO* pBmi2 = (BITMAPINFO*) new BYTE[dwSize];
	memmove(pBmi2, pBmi, dwSize);
	
	HDC hMonoDC = CreateCompatibleDC(m_hPlayDC); // must be monochrome
	ASSERT(hMonoDC);
	SCFillMonochromePalette(hMonoDC, (PPALETTEENTRY)pBmi2->bmiColors);

	// May create resource leak, as we don't call DeleteObject on hBm (see comments in the final block)
	hBm = CreateDIBitmap(hMonoDC, &pBmi2->bmiHeader, CBM_INIT, pBitsDW, pBmi2, DIB_RGB_COLORS);
	
	DeleteDC(hMonoDC);
	delete [] (BYTE*)pBmi2;
	ASSERT(hBm);

#ifdef _DEBUG
	{// check
		BITMAP bm;
		int iRes = GetObject(hBm, sizeof(BITMAP), &bm);
		ASSERT(iRes);
	}
#endif

	HBRUSH hBrush = CreatePatternBrush(hBm);
	ASSERT(hBrush);
	m_lpEnumHandleTable->objectHandle[pRec->ihBrush] = hBrush;

#if 0
	// Don't do this normal step of the code:
	//		DeleteObject(hBm);
	// It seems that XP's CreatePatternBrush does not make a copy of the given bitmap.
	// To prove it, activate this code, and you will see that GetObjectType
	// will fail on the LogBrushVerif2.lbHatch handle returned by GetObject.
	{
		// Check before delete
		LOGBRUSH LogBrushVerif1;
		ASSERT(::GetObject(hBrush, sizeof(LogBrushVerif1), &LogBrushVerif1));
		ASSERT(LogBrushVerif1.lbStyle==BS_PATTERN);
		int iType1 = GetObjectType((HGDIOBJ)LogBrushVerif1.lbHatch);
		ASSERT(iType1==OBJ_BITMAP);
		
		// Suppose we delete it
		DeleteObject(hBm);
		
		// Check after delete
		LOGBRUSH LogBrushVerif2;
		ASSERT(::GetObject(hBrush, sizeof(LogBrushVerif2), &LogBrushVerif2));
		ASSERT(LogBrushVerif2.lbStyle==BS_PATTERN);
		int iType2 = GetObjectType((HGDIOBJ)LogBrushVerif2.lbHatch);
		ASSERT(iType2==OBJ_BITMAP); // fails
	}
#endif

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEDIBPATTERNBRUSHPT()
{
	// TRACE0("**EMR_CREATEDIBPATTERNBRUSHPT\n");
	ASSERT(m_pRenderer);

#if 0
	// On NT.SP5, Same problem as monobrush
	SCPlayRecord();
#else
	EMRCREATEDIBPATTERNBRUSHPT *pRec = (EMRCREATEDIBPATTERNBRUSHPT*)m_pRecord;
	ASSERT(DIB_PAL_COLORS==pRec->iUsage || DIB_RGB_COLORS==pRec->iUsage);
	BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmi);

	// Note: On Windows 95 and Windows 98, "creating brushes from bitmaps or DIBs
	//	larger than 8x8 pixels is not supported". So we should restrict the pattern to 8x8.
	// But what if the EMF was created on NT/2K/XP? It seems that GDI doesn't shrink the
	// pattern in order to render the EMF OS-independently.

	DWORD dwSize = pRec->cbBmi + pRec->cbBits;
	// should agree with:
	// BITMAPINFOHEADER* pBmih = &pBmi->bmiHeader;
	// DWORD dwSize = pBmih->biSize +
	//						ColorTableSize(pBmih) +
	//						((pBmih->biSizeImage) ? pBmih->biSizeImage :
	//						abs(pBmih->biHeight)*WIDTHBYTES(pBmih->biWidth*pBmih->biBitCount));

	// May create resource leak, as we don't call GlobalFree on hMem
	// (in fact, this is the sole purpose of this code)
	HGLOBAL hMem = GlobalAlloc(GPTR, dwSize);
	BITMAPINFO* pBmi2 = (BITMAPINFO*)GlobalLock(hMem);
	memmove(pBmi2, pBmi, dwSize);

	HBRUSH hBrush = CreateDIBPatternBrushPt(pBmi2, pRec->iUsage);

	GlobalUnlock(hMem);
	ASSERT(hBrush);
	m_lpEnumHandleTable->objectHandle[pRec->ihBrush] = hBrush;

	#ifdef _DEBUG
	{// Check after play
		LOGBRUSH LogBrushVerif;
		ASSERT(::GetObject((HBRUSH)m_lpEnumHandleTable->objectHandle[pRec->ihBrush], sizeof(LogBrushVerif), &LogBrushVerif));
		ASSERT(LogBrushVerif.lbStyle==BS_DIBPATTERN);
	}
	#endif
#endif
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTCREATEPEN()
{
	// TRACE0("**EMR_EXTCREATEPEN\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// EMREXTCREATEPEN *pRec = (EMREXTCREATEPEN*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYTEXTOUTA()
{
	// TRACE0("**EMR_POLYTEXTOUTA\n");
	ASSERT(m_pRenderer);

	EMRPOLYTEXTOUTA *pRec = (EMRPOLYTEXTOUTA*)m_pRecord;
	for (int i=0; (i<pRec->cStrings); i++)
	{
		SCEmfTextoutA((EMREXTTEXTOUTA*)pRec, &pRec->aemrtext[i]);
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYTEXTOUTW()
{
	// TRACE0("**EMR_POLYTEXTOUTW\n");
	ASSERT(m_pRenderer);

	EMRPOLYTEXTOUTW *pRec = (EMRPOLYTEXTOUTW*)m_pRecord;
	for (int i=0; (i<pRec->cStrings); i++)
	{
		SCEmfTextoutW((EMREXTTEXTOUTW*)pRec, &pRec->aemrtext[i]);
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMMODE()
{
	// TRACE0("**EMR_SETICMMODE\n");
	ASSERT(m_pRenderer);

//		EMRSETICMMODE *pRec = (EMRSETICMMODE*)m_pRecord;

	// MSDN: see "DIBINFO.C"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATECOLORSPACE()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRCREATECOLORSPACE *pRec = (EMRCREATECOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATECOLORSPACEW()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

	//EMRCREATECOLORSPACEW *pRec = (EMRCREATECOLORSPACEW*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETCOLORSPACE()
{
	// TRACE0("**EMR_SETCOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRSELECTCOLORSPACE *pRec = (EMRSELECTCOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDELETECOLORSPACE()
{
	// TRACE0("**EMR_DELETECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRDELETECOLORSPACE *pRec = (EMRDELETECOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGLSRECORD()
{
	// TRACE0("**EMR_GLSRECORD\n");
	ASSERT(m_pRenderer);

	SC_BRKRESULT error(SC_BRK_NOERROR);
	EMRGLSRECORD *pRec = (EMRGLSRECORD*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return error;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGLSBOUNDEDRECORD()
{
	// TRACE0("**EMR_GLSBOUNDEDRECORD\n");
	ASSERT(m_pRenderer);

//		EMRGLSBOUNDEDRECORD *pRec = (EMRGLSBOUNDEDRECORD*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPIXELFORMAT()
{
	// TRACE0("**EMR_PIXELFORMAT\n");
	ASSERT(m_pRenderer);

//		EMRPIXELFORMAT *pRec = (EMRPIXELFORMAT*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDRAWESCAPE()
{
	// TRACE0("**EMR_DRAWESCAPE\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_105
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTESCAPE()
{
	// TRACE0("**EMR_EXTESCAPE\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_106
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTARTDOC()
{
	// TRACE0("**EMR_STARTDOC\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_107
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSMALLTEXTOUT()
{
	// TRACE0("**EMR_SMALLTEXTOUT\n");
	ASSERT(m_pRenderer);

	// WARNING: it's SCEMF_RESERVED_108
	SCEMRSMALLTEXTOUTA *pRec = (SCEMRSMALLTEXTOUTA*)m_pRecord;
	// TODO: review this line
	SCEMRSMALLTEXTOUTA2 *pTextRec2 = (SCEMRSMALLTEXTOUTA2*)m_pRecord;

	// get the EMRTEXT
	SCEMRSMALLTEXTOUTA *pTextRec = pRec;

	// number of characters	
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
	{
		// We don't know the behavior of ExtTextOut:
		// - if number of characters is 0
		// - if, moreover, TA_UPDATECP is set in the DC
#if 0
		// if required, play the record
		SCPlayRecord();
#endif
		return SC_BRK_NOERROR;
	}

	UINT uiOptions = pRec->fOptions; // we might have to alter it
	// TODO: review this line
	BOOL bUnicode = (uiOptions == 0x100);

	// Build the string
	LPWSTR pwStr = NULL;
	if (bUnicode) 
	{
		// The string is in unicode -> we let it as is.
		// Offset is from the beginning of the struct and is in bytes.
		// 	
		// Get the unicode string
		pwStr = (LPWSTR)pTextRec->Text;

#if 0
		// TODO:
		// Translate glyph indices to code points when the rasterizer does not
		// support string measurement for glyph indices.
		if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
		{
			int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pString, iNbChars);
			if (iRes)
				uiOptions &= ~ETO_GLYPH_INDEX;
		}
#endif
	} else
	{
		// The string is single-byte encoded.
		// Get adress of the string	then construct the string.			
		LPSTR pString = (LPSTR)pTextRec->Text;
		// TODO: review this line
		if (uiOptions==0x204)
			pString = (char *)pTextRec2->Text;

		pwStr = new wchar_t[iNbChars+1];
		if (pwStr)
		{// We will use UNICODE strings exclusively
			pwStr[iNbChars] = 0;
			int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pString, iNbChars, pwStr, iNbChars+1);
			#ifdef _DEBUG
			if (0==iNum)
			{
				DWORD dwError = GetLastError();
			}
			#endif
			ASSERT(iNum==iNbChars);
		}
	}
	ASSERT(pwStr);

	// Get intercharacter spacing array.
	// Be careful ! there are cases where OffDx==0.
	// TODO: review this line
	DWORD OffDx = 0;//pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	//if (0==OffDx)
	{
		// If the lpDx parameter is NULL, the ExtTextOut function uses the default
		// spacing between characters. So we do.
		//
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	} // else use distances between origins of adjacent character cells as passed to ExtTextOut.

	CRect rcText;
	if (uiOptions==0x204)
	{
		rcText.CopyRect((LPRECT)&pTextRec2->rclBoundsSC);
	} else
	{
		uiOptions &= ~ETO_CLIPPED; // we don't have the rectangle
		rcText.SetRectEmpty();
	}
	if (GM_COMPATIBLE==pTextRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPRECT)&rcText, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPRECT)&rcText, pwStr, iNbChars, lpDx, 1, 1);
	

	// Clean up
	if (!bUnicode)
		delete [] pwStr;
	if (0==OffDx)
		delete [] lpDx;

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFORCEUFIMAPPING()
{
	// TRACE0("**EMR_FORCEUFIMAPPING\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Danger if it writes to the DC
	// Worse if it attempts escape-like behavior
	//SCPlayRecord();

	// Anyway, now it's SCEMF_RESERVED_109
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfNAMEDESCAPE()
{
	// TRACE0("**EMR_NAMEDESCAPE\n");
	ASSERT(m_pRenderer);

	// Do not play escape
	// Anyway, now it's SCEMF_RESERVED_110
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCOLORCORRECTPALETTE()
{
	// TRACE0("**EMR_COLORCORRECTPALETTE\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMPROFILEA()
{
	// TRACE0("**EMR_SETICMPROFILEA\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMPROFILEW()
{
	// TRACE0("**EMR_SETICMPROFILEW\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfALPHABLEND()
{
	// TRACE0("**EMR_ALPHABLEND\n");
	ASSERT(m_pRenderer);

	EMRALPHABLEND *pRec = (EMRALPHABLEND*)m_pRecord;

	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);

	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// Destination
		CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);

		m_pRenderer->SCDrawImageAlpha(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc,
			pRec->dwRop, &pRec->xformSrc, pRec->crBkColorSrc);
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfALPHADIBBLEND()
{
	// TRACE0("**EMR_SETLAYOUT\n");
	ASSERT(m_pRenderer);

	// Now it's SCEMF_SETLAYOUT
	//DWORD SetLayout(
	//	HDC hdc,             // handle to DC
	//	DWORD dwLayout,      // layout options
	//	);
	// dwLayout==LAYOUT_BITMAPORIENTATIONPRESERVED Disables any reflection during BitBlt
	//											   and StretchBlt operations. 
	//           LAYOUT_RTL						   Sets the default horizontal layout
	//											   to be right to left. 
	// OSes: Windows 2000 and later, Windows 98 and later

	// TODO_EMF: horizontal reflection when the LAYOUT_RTL flag is selected
	// m_pRenderer->SCSetLayout(((EMRSETLAYOUT*)m_pRecord)->iMode);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfTRANSPARENTBLT()
{
	// TRACE0("**EMR_TRANSPARENTBLT\n");
	ASSERT(m_pRenderer);

	EMRTRANSPARENTBLT *pRec = (EMRTRANSPARENTBLT*)m_pRecord;
	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);

	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// Destination
		CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);

		m_pRenderer->SCDrawImageTransparent(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc,
			pRec->dwRop, &pRec->xformSrc, pRec->crBkColorSrc);
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfTRANSPARENTDIB()
{
	// TRACE0("**EMR_TRANSPARENTDIB\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	// Anyway, now it's SCEMF_RESERVED_117
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGRADIENTFILL()
{
	// TRACE0("**EMR_GRADIENTFILL\n");
	ASSERT(m_pRenderer);

	EMRGRADIENTFILL *pRec = (EMRGRADIENTFILL*)m_pRecord;

	LPBYTE pMesh = (LPBYTE)&pRec->Ver[pRec->nVer];
	m_pRenderer->SCGradientFill(pRec->Ver, pRec->nVer, pMesh, pRec->nTri, pRec->ulMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETLINKEDUFIS()
{
	// TRACE0("**EMR_SETLINKEDUFIS\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	// Anyway, now it's SCEMF_RESERVED_119
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTJUSTIFICATION()
{
	// TRACE0("**EMR_SETTEXTJUSTIFICATION\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	// Anyway, now it's SCEMF_RESERVED_120
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCOLORMATCHTOTARGETW()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		COLORMATCHTOTARGETW *pRec = (COLORMATCHTOTARGETW*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnBrkUNKRecord(long lMsg)
{
	// TRACE0("**OnBrkUNKRecord\n");
	ASSERT(m_pRenderer);

	UNUSED(lMsg);   // unused in release builds

	// By default, play unkown record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

//////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////

// called before enumeration
SC_BRKRESULT CSCEMFgdiParser::SCBeginBreak(HDC hDC, LPVOID lpData, CONST RECT *lpRect)
{
	SC_BRKRESULT error(SC_BRK_NOERROR);

	ASSERT(m_pRenderer);
	if (!m_pRenderer)
		return SC_BRK_NORENDERER;

	GetWorldTransform(hDC, &m_xformOrig);

	// call inherited
	error = SCBrkEMF::SCBeginBreak(hDC, lpData, lpRect);

	// before enumeration
	// clone the DC so that this class does not write on the destination DC
	m_hDestDC = hDC;					 // for GDI+

#ifdef SC_USE_DCCLONER
	// TODO: fix the bitmap problem in the cloner
	m_pCloneDC = new SCDCCloner(hDC);
	ASSERT(m_pCloneDC);
	m_hPlayDC = m_pCloneDC->SCGetHDC();	 // for GDI
#else
	m_hPlayDC = hDC;					 // for GDI
#endif

	ASSERT(m_hPlayDC && m_hDestDC);
		
	return error;
}


//////////////////////////////////////////////////////////////////////
// Message map
//////////////////////////////////////////////////////////////////////
SC_BEGIN_MESSAGE_MAP(CSCEMFgdiParser, SCBrkEMF)
	ON_VECTRECORD(SCEMF_HEADER, OnEmfHEADER)
	ON_VECTRECORD(SCEMF_POLYBEZIER, OnEmfPOLYBEZIER)
	ON_VECTRECORD(SCEMF_POLYGON, OnEmfPOLYGON)
	ON_VECTRECORD(SCEMF_POLYLINE, OnEmfPOLYLINE)
	ON_VECTRECORD(SCEMF_POLYBEZIERTO, OnEmfPOLYBEZIERTO)
	ON_VECTRECORD(SCEMF_POLYLINETO, OnEmfPOLYLINETO)
	ON_VECTRECORD(SCEMF_POLYPOLYLINE, OnEmfPOLYPOLYLINE)
	ON_VECTRECORD(SCEMF_POLYPOLYGON, OnEmfPOLYPOLYGON)
	ON_VECTRECORD(SCEMF_SETWINDOWEXTEX, OnEmfSETWINDOWEXTEX)
	ON_VECTRECORD(SCEMF_SETWINDOWORGEX, OnEmfSETWINDOWORGEX)
	ON_VECTRECORD(SCEMF_SETVIEWPORTEXTEX, OnEmfSETVIEWPORTEXTEX)
	ON_VECTRECORD(SCEMF_SETVIEWPORTORGEX, OnEmfSETVIEWPORTORGEX)
	ON_VECTRECORD(SCEMF_SETBRUSHORGEX, OnEmfSETBRUSHORGEX)
	ON_VECTRECORD(SCEMF_EOF, OnEmfEOF)
	ON_VECTRECORD(SCEMF_SETPIXELV, OnEmfSETPIXELV)
	ON_VECTRECORD(SCEMF_SETMAPPERFLAGS, OnEmfSETMAPPERFLAGS)
	ON_VECTRECORD(SCEMF_SETMAPMODE, OnEmfSETMAPMODE)
	ON_VECTRECORD(SCEMF_SETBKMODE, OnEmfSETBKMODE)
	ON_VECTRECORD(SCEMF_SETPOLYFILLMODE, OnEmfSETPOLYFILLMODE)
	ON_VECTRECORD(SCEMF_SETROP2, OnEmfSETROP2)
	ON_VECTRECORD(SCEMF_SETSTRETCHBLTMODE, OnEmfSETSTRETCHBLTMODE)
	ON_VECTRECORD(SCEMF_SETTEXTALIGN, OnEmfSETTEXTALIGN)
	ON_VECTRECORD(SCEMF_SETCOLORADJUSTMENT, OnEmfSETCOLORADJUSTMENT)
	ON_VECTRECORD(SCEMF_SETTEXTCOLOR, OnEmfSETTEXTCOLOR)
	ON_VECTRECORD(SCEMF_SETBKCOLOR, OnEmfSETBKCOLOR)
	ON_VECTRECORD(SCEMF_OFFSETCLIPRGN, OnEmfOFFSETCLIPRGN)
	ON_VECTRECORD(SCEMF_MOVETOEX, OnEmfMOVETOEX)
	ON_VECTRECORD(SCEMF_SETMETARGN, OnEmfSETMETARGN)
	ON_VECTRECORD(SCEMF_EXCLUDECLIPRECT, OnEmfEXCLUDECLIPRECT)
	ON_VECTRECORD(SCEMF_INTERSECTCLIPRECT, OnEmfINTERSECTCLIPRECT)
	ON_VECTRECORD(SCEMF_SCALEVIEWPORTEXTEX, OnEmfSCALEVIEWPORTEXTEX)
	ON_VECTRECORD(SCEMF_SCALEWINDOWEXTEX, OnEmfSCALEWINDOWEXTEX)
	ON_VECTRECORD(SCEMF_SAVEDC, OnEmfSAVEDC)
	ON_VECTRECORD(SCEMF_RESTOREDC, OnEmfRESTOREDC)
	ON_VECTRECORD(SCEMF_SETWORLDTRANSFORM, OnEmfSETWORLDTRANSFORM)
	ON_VECTRECORD(SCEMF_MODIFYWORLDTRANSFORM, OnEmfMODIFYWORLDTRANSFORM)
	ON_VECTRECORD(SCEMF_SELECTOBJECT, OnEmfSELECTOBJECT)
	ON_VECTRECORD(SCEMF_CREATEPEN, OnEmfCREATEPEN)
	ON_VECTRECORD(SCEMF_CREATEBRUSHINDIRECT, OnEmfCREATEBRUSHINDIRECT)
	ON_VECTRECORD(SCEMF_DELETEOBJECT, OnEmfDELETEOBJECT)
	ON_VECTRECORD(SCEMF_ANGLEARC, OnEmfANGLEARC)
	ON_VECTRECORD(SCEMF_ELLIPSE, OnEmfELLIPSE)
	ON_VECTRECORD(SCEMF_RECTANGLE, OnEmfRECTANGLE)
	ON_VECTRECORD(SCEMF_ROUNDRECT, OnEmfROUNDRECT)
	ON_VECTRECORD(SCEMF_ARC, OnEmfARC)
	ON_VECTRECORD(SCEMF_CHORD, OnEmfCHORD)
	ON_VECTRECORD(SCEMF_PIE, OnEmfPIE)
	ON_VECTRECORD(SCEMF_SELECTPALETTE, OnEmfSELECTPALETTE)
	ON_VECTRECORD(SCEMF_CREATEPALETTE, OnEmfCREATEPALETTE)
	ON_VECTRECORD(SCEMF_SETPALETTEENTRIES, OnEmfSETPALETTEENTRIES)
	ON_VECTRECORD(SCEMF_RESIZEPALETTE, OnEmfRESIZEPALETTE)
	ON_VECTRECORD(SCEMF_REALIZEPALETTE, OnEmfREALIZEPALETTE)
	ON_VECTRECORD(SCEMF_EXTFLOODFILL, OnEmfEXTFLOODFILL)
	ON_VECTRECORD(SCEMF_LINETO, OnEmfLINETO)
	ON_VECTRECORD(SCEMF_ARCTO, OnEmfARCTO)
	ON_VECTRECORD(SCEMF_POLYDRAW, OnEmfPOLYDRAW)
	ON_VECTRECORD(SCEMF_SETARCDIRECTION, OnEmfSETARCDIRECTION)
	ON_VECTRECORD(SCEMF_SETMITERLIMIT, OnEmfSETMITERLIMIT)
	ON_VECTRECORD(SCEMF_BEGINPATH, OnEmfBEGINPATH)
	ON_VECTRECORD(SCEMF_ENDPATH, OnEmfENDPATH)
	ON_VECTRECORD(SCEMF_CLOSEFIGURE, OnEmfCLOSEFIGURE)
	ON_VECTRECORD(SCEMF_FILLPATH, OnEmfFILLPATH)
	ON_VECTRECORD(SCEMF_STROKEANDFILLPATH, OnEmfSTROKEANDFILLPATH)
	ON_VECTRECORD(SCEMF_STROKEPATH, OnEmfSTROKEPATH)
	ON_VECTRECORD(SCEMF_FLATTENPATH, OnEmfFLATTENPATH)
	ON_VECTRECORD(SCEMF_WIDENPATH, OnEmfWIDENPATH)
	ON_VECTRECORD(SCEMF_SELECTCLIPPATH, OnEmfSELECTCLIPPATH)
	ON_VECTRECORD(SCEMF_ABORTPATH, OnEmfABORTPATH)
	ON_VECTRECORD(SCEMF_GDICOMMENT, OnEmfGDICOMMENT)
	ON_VECTRECORD(SCEMF_FILLRGN, OnEmfFILLRGN)
	ON_VECTRECORD(SCEMF_FRAMERGN, OnEmfFRAMERGN)
	ON_VECTRECORD(SCEMF_INVERTRGN, OnEmfINVERTRGN)
	ON_VECTRECORD(SCEMF_PAINTRGN, OnEmfPAINTRGN)
	ON_VECTRECORD(SCEMF_EXTSELECTCLIPRGN, OnEmfEXTSELECTCLIPRGN)
	ON_VECTRECORD(SCEMF_BITBLT, OnEmfBITBLT)
	ON_VECTRECORD(SCEMF_STRETCHBLT, OnEmfSTRETCHBLT)
	ON_VECTRECORD(SCEMF_MASKBLT, OnEmfMASKBLT)
	ON_VECTRECORD(SCEMF_PLGBLT, OnEmfPLGBLT)
	ON_VECTRECORD(SCEMF_SETDIBITSTODEVICE, OnEmfSETDIBITSTODEVICE)
	ON_VECTRECORD(SCEMF_STRETCHDIBITS, OnEmfSTRETCHDIBITS)
	ON_VECTRECORD(SCEMF_EXTCREATEFONTINDIRECTW, OnEmfEXTCREATEFONTINDIRECTW)
	ON_VECTRECORD(SCEMF_EXTTEXTOUTA, OnEmfEXTTEXTOUTA)
	ON_VECTRECORD(SCEMF_EXTTEXTOUTW, OnEmfEXTTEXTOUTW)
	ON_VECTRECORD(SCEMF_POLYBEZIER16, OnEmfPOLYBEZIER16)
	ON_VECTRECORD(SCEMF_POLYGON16, OnEmfPOLYGON16)
	ON_VECTRECORD(SCEMF_POLYLINE16, OnEmfPOLYLINE16)
	ON_VECTRECORD(SCEMF_POLYBEZIERTO16, OnEmfPOLYBEZIERTO16)
	ON_VECTRECORD(SCEMF_POLYLINETO16, OnEmfPOLYLINETO16)
	ON_VECTRECORD(SCEMF_POLYPOLYLINE16, OnEmfPOLYPOLYLINE16)
	ON_VECTRECORD(SCEMF_POLYPOLYGON16, OnEmfPOLYPOLYGON16)
	ON_VECTRECORD(SCEMF_POLYDRAW16, OnEmfPOLYDRAW16)
	ON_VECTRECORD(SCEMF_CREATEMONOBRUSH, OnEmfCREATEMONOBRUSH)
	ON_VECTRECORD(SCEMF_CREATEDIBPATTERNBRUSHPT, OnEmfCREATEDIBPATTERNBRUSHPT)
	ON_VECTRECORD(SCEMF_EXTCREATEPEN, OnEmfEXTCREATEPEN)
	ON_VECTRECORD(SCEMF_POLYTEXTOUTA, OnEmfPOLYTEXTOUTA)
	ON_VECTRECORD(SCEMF_POLYTEXTOUTW, OnEmfPOLYTEXTOUTW)
	ON_VECTRECORD(SCEMF_SETICMMODE, OnEmfSETICMMODE)
	ON_VECTRECORD(SCEMF_CREATECOLORSPACE, OnEmfCREATECOLORSPACE)
	ON_VECTRECORD(SCEMF_SETCOLORSPACE, OnEmfSETCOLORSPACE)
	ON_VECTRECORD(SCEMF_DELETECOLORSPACE, OnEmfDELETECOLORSPACE)
	ON_VECTRECORD(SCEMF_GLSRECORD, OnEmfGLSRECORD)
	ON_VECTRECORD(SCEMF_GLSBOUNDEDRECORD, OnEmfGLSBOUNDEDRECORD)
	ON_VECTRECORD(SCEMF_PIXELFORMAT, OnEmfPIXELFORMAT)
	ON_VECTRECORD(SCEMF_DRAWESCAPE, OnEmfDRAWESCAPE)
	ON_VECTRECORD(SCEMF_EXTESCAPE, OnEmfEXTESCAPE)
	ON_VECTRECORD(SCEMF_STARTDOC, OnEmfSTARTDOC)
	ON_VECTRECORD(SCEMF_SMALLTEXTOUT, OnEmfSMALLTEXTOUT)
	ON_VECTRECORD(SCEMF_FORCEUFIMAPPING, OnEmfFORCEUFIMAPPING)
	ON_VECTRECORD(SCEMF_NAMEDESCAPE, OnEmfNAMEDESCAPE)
	ON_VECTRECORD(SCEMF_COLORCORRECTPALETTE, OnEmfCOLORCORRECTPALETTE)
	ON_VECTRECORD(SCEMF_SETICMPROFILEA, OnEmfSETICMPROFILEA)
	ON_VECTRECORD(SCEMF_SETICMPROFILEW, OnEmfSETICMPROFILEW)
	ON_VECTRECORD(SCEMF_ALPHABLEND, OnEmfALPHABLEND)
	ON_VECTRECORD(SCEMF_ALPHADIBBLEND, OnEmfALPHADIBBLEND)
	ON_VECTRECORD(SCEMF_TRANSPARENTBLT, OnEmfTRANSPARENTBLT)
	ON_VECTRECORD(SCEMF_TRANSPARENTDIB, OnEmfTRANSPARENTDIB)
	ON_VECTRECORD(SCEMF_GRADIENTFILL, OnEmfGRADIENTFILL)
	ON_VECTRECORD(SCEMF_SETLINKEDUFIS, OnEmfSETLINKEDUFIS)
	ON_VECTRECORD(SCEMF_SETTEXTJUSTIFICATION, OnEmfSETTEXTJUSTIFICATION)
	ON_VECTRECORD(SCEMF_COLORMATCHTOTARGETW, OnEmfCOLORMATCHTOTARGETW)
	ON_VECTRECORD(SCEMF_CREATECOLORSPACEW, OnEmfCREATECOLORSPACEW)
SC_END_MESSAGE_MAP()

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions