/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    Convert WordPerfect files into LaTeX.			      *
 * modul:       pass1wmf.cc                                                   *
 * description: Opens Windows MetaFile and Enhanced Windows Metafile	      *
 * licency:     GPL		                                              *
 ******************************************************************************/
// See https://en.wikipedia.org/wiki/Windows_Metafile
//     http://www.fileformat.info/format/wmf/egff.htm
// for WMF description.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

//Atoms library
#include "stringa.h"
#include "struct.h"

#include "wp2latex.h"
#include "raster.h"
#include "images/vecimage.h"
#include "images.h"
#include "cp_lib/cptran.h"

#include "wmfsupp.h"


//#define TEXT_DEBUG_FRAMES

#ifndef M_PI
 #define M_PI        3.14159265358979323846
#endif

#ifndef MAX
 #define MAX(value1,value2) ((value1 > value2) ? value1 : value2)
#endif


void WMF_STRETCHDIB(Image &Img, FILE *f, uint32_t ParamFilePos);
void WMF_DIBSTRETCHBLT(Image &Img, FILE *f, uint32_t ParamFilePos);
void WMF_DIBBITBLT(Image &Img, FILE *f, uint32_t ParamFilePos);
void LoadBmpStream(Image &Img,FILE *f, uint32_t Header_bfOffBits);

int SavePictureEPS(const char *Name, const Image &Img);
#ifdef _DEBUG
 //int SavePictureBMP(const char *Name, const Image &Img);
#endif


/// WMF codepages: see this https://docs.microsoft.com/en-us/previous-versions/aa915041(v=msdn.10)?redirectedfrom=MSDN
typedef  enum  
{ 
  ANSI_CHARSET = 0x00000000,		///< 1252 FS_LATIN1 
  DEFAULT_CHARSET = 0x00000001,
  SYMBOL_CHARSET = 0x00000002, 
  MAC_CHARSET = 0x0000004D, 
  SHIFTJIS_CHARSET = 0x00000080,	///< cp932 FS_JISJAPAN
  HANGUL_CHARSET = 0x00000081,		///< cp949 FS_WANSUNG
  JOHAB_CHARSET = 0x00000082,		///< 1361 FS_JOHAB
  GB2312_CHARSET = 0x00000086,		///< 936 FS_CHINESESIMP 
  CHINESEBIG5_CHARSET = 0x00000088,	///< 950 FS_CHINESETRAD
  GREEK_CHARSET = 0x000000A1,		///< 1253 FS_GREEK
  TURKISH_CHARSET = 0x000000A2,		///< 1254 FS_TURKISH
  VIETNAMESE_CHARSET = 0x000000A3, 
  HEBREW_CHARSET = 0x000000B1,		///< cp1255 FS_HEBREW
  ARABIC_CHARSET = 0x000000B2,		///< cp1256 FS_ARABIC
  BALTIC_CHARSET = 0x000000BA,		///< cp1257 FS_BALTIC 
  RUSSIAN_CHARSET = 0x000000CC,		///< cp1251 FS_CYRILLIC
  THAI_CHARSET = 0x000000DE,		///< cp874 FS_THAI
  EASTEUROPE_CHARSET = 0x000000EE,	///< cp1250 FS_LATIN2
  OEM_CHARSET = 0x000000FF 
} TCharacterSet;


typedef  enum  
{ 
  PS_COSMETIC = 0x0000, 
  PS_ENDCAP_ROUND = 0x0000, 
  PS_JOIN_ROUND = 0x0000, 
  PS_SOLID = 0x0000, 
  PS_DASH = 0x0001, 
  PS_DOT = 0x0002, 
  PS_DASHDOT = 0x0003, 
  PS_DASHDOTDOT = 0x0004, 
  PS_NULL = 0x0005, 
  PS_INSIDEFRAME = 0x0006, 
  PS_USERSTYLE = 0x0007, 
  PS_ALTERNATE = 0x0008, 
  PS_ENDCAP_SQUARE = 0x0100, 
  PS_ENDCAP_FLAT = 0x0200, 
  PS_JOIN_BEVEL = 0x1000,
  PS_JOIN_MITER = 0x2000,
#if !defined(__BORLANDC__)
  PS_GEOMETRIC = 0x00010000	// used in EMF
#endif
} TPenStyle;


typedef  enum  
{ 
  BS_SOLID = 0x0000, 
  BS_NULL = 0x0001, 
  BS_HATCHED = 0x0002, 
  BS_PATTERN = 0x0003, 
  BS_INDEXED = 0x0004, 
  BS_DIBPATTERN = 0x0005, 
  BS_DIBPATTERNPT = 0x0006, 
  BS_PATTERN8X8 = 0x0007, 
  BS_DIBPATTERN8X8 = 0x0008, 
  BS_MONOPATTERN = 0x0009 
} TBrushStyle;

typedef  enum 
{ 
  HS_HORIZONTAL = 0x0000, 
  HS_VERTICAL = 0x0001, 
  HS_FDIAGONAL = 0x0002, 
  HS_BDIAGONAL = 0x0003, 
  HS_CROSS = 0x0004, 
  HS_DIAGCROSS = 0x0005 
} HatchStyle; 


/// Get scalling factor for a given map mode.
/// @return	Scalling factor.
float GetScale2PSU(const TMapMode MapM)
{
  switch(MapM)
  {
    case MM_TEXT:	return 1; 
    case MM_LOMETRIC:   return mm2PSu(0.1f);
    case MM_HIMETRIC:   return mm2PSu(0.01f);
    case MM_LOENGLISH:  return mm2PSu(0.254f);
    case MM_HIENGLISH:  return mm2PSu(0.0254f);
    case MM_TWIPS:	return mm2PSu(0.017638889f);
    case MM_ISOTROPIC:
    case MM_ANISOTROPIC: break;
  }
  return 1;
}


////////////////////////////////////////////////


typedef struct _StandardMetaRecord
{
    uint32_t Size;          /**< Total size of the record in WORDs */
    uint16_t  Function;      /**< Function number (defined in WINDOWS.H) */
    uint32_t ParamFilePos;
    //uint16_t  Parameters[];  /**< Parameter values passed to function */
} WMFRECORD;



class TconvertedPass1_WMF: public TconvertedPass1_xMF
     {
public:
     TconvertedPass1_WMF(void) {PositionX=PositionY=0;}

     virtual int Convert_first_pass(void);
     virtual int Dispatch(int FuncNo, const void *arg);
     int LoadImageWMF(void);

     int16_t PositionX, PositionY;
     _StandardMetaRecord WmfRec;

protected:
     bool CheckWmfRecSz(uint16_t WSizeRequest);
     void ReportCorruptedObj(const char *ObjName);

     void parse_Arc(VectorList &VectList);
     void parse_DibBitBlt(void);
     void parse_CreateBrushIndirect(void);
     void parse_CreatePalette(void);
     void parse_CreatePenIndirect(void);
     void parse_DeleteObject(void);
     void parse_DibCreatePatternBrush(void);
     void parse_DibStretchBlt(void);
     void parse_Ellipse(VectorList &VectList);
     void parse_FontIndirect(void);
     void parse_Chord(void);
     void parse_LineTo(VectorList &VectList);
     void parse_MoveTo(void);
     void parse_SetPolyFillMode(void);
     void parse_Pie(VectorList &VectList);
     void parse_Polygon(VectorList &VectList);
     void parse_PolyPolygon(VectorList &VectList);
     void parse_PolyLine(VectorList &VectList);
     void wmf_ESCAPE(void);
     void parse_ExtTextOut(VectorList &VectList);
     void parse_Rectangle(VectorList &VectList);
     void parse_RestoreDC(void);
     void parse_RoundRect(VectorList &VectList);
     void parse_SaveDC(void);
     void parse_SelectObject(void);
     void parse_SelectPalette(void);
     void parse_SetPixel(VectorList &VectList);
     void parse_SetTextAlign(void);
     void parse_SetBkColor(void);
     void parse_SetBkMode(void);
     void parse_SetMapMode(void);
     void parse_SetTextColor(void);
     void parse_SetWindowExt(void);
     void parse_SetWindowOrg(void);
     void parse_TextOut(VectorList &VectList);
     void StretchDIBits(void);
     };


/* Register WMF translators here. */
TconvertedPass1 *Factory_WMF(void) {return new TconvertedPass1_WMF;}
FFormatTranslator FormatWMFWrapper("WMF",Factory_WMF);

typedef struct _WindowsMetaHeader
{
  uint16_t  FileType;       ///< Type of metafile (0=memory, 1=disk).
  uint16_t  HeaderSize;     ///< Size of header in WORDS (always 9).
  uint16_t  Version;        ///< Version of Microsoft Windows used.
  uint32_t FileSize;       ///< Total size of the metafile in WORDs.
  uint16_t  NumOfObjects;   ///< Number of objects in the file.
  uint32_t MaxRecordSize;  ///< The size of largest record in WORDs.
  uint16_t  NumOfParams;    ///< Not Used (always 0).
} WMFHEAD;


inline long LoadWMFHeader(FILE *f, _WindowsMetaHeader & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(_WindowsMetaHeader),f));
#else
return(loadstruct(f,"wwwdwdw", &SU.FileType, &SU.HeaderSize,
	&SU.Version, &SU.FileSize, &SU.NumOfObjects, &SU.MaxRecordSize, &SU.NumOfParams));
#endif
}


/** Load header for one WMF record. */
bool LoadWmfRecord(FILE *f, _StandardMetaRecord &WmfRec)
{
  Rd_dword(f,&WmfRec.Size);
  if(feof(f) || WmfRec.Size<3) return false;
  Rd_word(f,&WmfRec.Function);
  WmfRec.ParamFilePos = ftell(f);
  return true;
}


void TconvertedPass1_WMF::ReportCorruptedObj(const char *ObjName)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::ReportCorruptedObj() ");fflush(log);
#endif
  if(err==NULL) return;
  perc.Hide();
  fprintf(err,_("\nError: corrupted wmf object: \"%s\"."), chk(ObjName));
}


typedef  enum  
{ 
  NEWFRAME = 0x0001, 
  ABORTDOC = 0x0002, 
  NEXTBAND = 0x0003, 
  SETCOLORTABLE = 0x0004, 
  GETCOLORTABLE = 0x0005, 
  FLUSHOUT = 0x0006, 
  DRAFTMODE = 0x0007, 
  QUERYESCSUPPORT = 0x0008, 
  SETABORTPROC = 0x0009, 
  STARTDOC = 0x000A, 
  ENDDOC = 0x000B, 
  GETPHYSPAGESIZE = 0x000C, 
  GETPRINTINGOFFSET = 0x000D, 
  GETSCALINGFACTOR = 0x000E, 
  META_ESCAPE_ENHANCED_METAFILE = 0x000F, 
  SETPENWIDTH = 0x0010, 
  SETCOPYCOUNT = 0x0011, 
  SETPAPERSOURCE = 0x0012, 
  PASSTHROUGH = 0x0013, 
  GETTECHNOLOGY = 0x0014, 
  SETLINECAP = 0x0015, 
  SETLINEJOIN = 0x0016, 
  SETMITERLIMIT = 0x0017, 
  BANDINFO = 0x0018, 
  DRAWPATTERNRECT = 0x0019, 
  GETVECTORPENSIZE = 0x001A, 
  GETVECTORBRUSHSIZE = 0x001B, 
  ENABLEDUPLEX = 0x001C, 
  GETSETPAPERBINS = 0x001D, 
  GETSETPRINTORIENT = 0x001E, 
  ENUMPAPERBINS = 0x001F, 
  SETDIBSCALING = 0x0020, 
  EPSPRINTING = 0x0021, 
  ENUMPAPERMETRICS = 0x0022, 
  GETSETPAPERMETRICS = 0x0023,
  POSTSCRIPT_DATA = 0x0025, 
  POSTSCRIPT_IGNORE = 0x0026, 
  GETDEVICEUNITS = 0x002A, 
  GETEXTENDEDTEXTMETRICS = 0x0100, 
  GETPAIRKERNTABLE = 0x0102, 
  EXTTEXTOUT = 0x0200, 
  GETFACENAME = 0x0201, 
  DOWNLOADFACE = 0x0202, 
  METAFILE_DRIVER = 0x0801, 
  QUERYDIBSUPPORT = 0x0C01, 
  BEGIN_PATH = 0x1000, 
  CLIP_TO_PATH = 0x1001, 
  END_PATH = 0x1002, 
  OPEN_CHANNEL = 0x100E, 
  DOWNLOADHEADER = 0x100F, 
  CLOSE_CHANNEL = 0x1010, 
  POSTSCRIPT_PASSTHROUGH = 0x1013, 
  ENCAPSULATED_POSTSCRIPT = 0x1014, 
  POSTSCRIPT_IDENTIFY = 0x1015, 
  POSTSCRIPT_INJECTION = 0x1016, 
  CHECKJPEGFORMAT = 0x1017, 
  CHECKPNGFORMAT = 0x1018, 
  GET_PS_FEATURESETTING = 0x1019, 
  MXDC_ESCAPE = 0x101A, 
  SPCLPASSTHROUGH2 = 0x11D8 
} MetafileEscapes;


const char *DecodeEscType(uint16_t EscType)
{
  switch(EscType)
  {
    case NEWFRAME: return "NEWFRAME";
    case ABORTDOC: return "ABORTDOC";
    case NEXTBAND: return "NEXTBAND";
    case SETCOLORTABLE: return "SETCOLORTABLE";
    case GETCOLORTABLE: return "GETCOLORTABLE"; 
    case FLUSHOUT: return "FLUSHOUT"; 
    case DRAFTMODE: return "DRAFTMODE";
    case QUERYESCSUPPORT: return "QUERYESCSUPPORT";
    case SETABORTPROC: return "SETABORTPROC";
    case STARTDOC: return "STARTDOC";
    case ENDDOC: return "ENDDOC";
    case GETPHYSPAGESIZE: return "GETPHYSPAGESIZE";
    case GETPRINTINGOFFSET: return "GETPRINTINGOFFSET";
    case GETSCALINGFACTOR: return "GETSCALINGFACTOR";
    case META_ESCAPE_ENHANCED_METAFILE: return "META_ESCAPE_ENHANCED_METAFILE";
    case SETPENWIDTH: return "SETPENWIDTH";
    case SETCOPYCOUNT: return "SETCOPYCOUNT";
    case SETPAPERSOURCE: return "SETPAPERSOURCE";
    case PASSTHROUGH: return "PASSTHROUGH";
    case GETTECHNOLOGY: return "GETTECHNOLOGY";
    case SETLINECAP: return "SETLINECAP";
    case SETLINEJOIN: return "SETLINEJOIN";
    case SETMITERLIMIT: return "SETMITERLIMIT";
    case BANDINFO: return "BANDINFO";
    case DRAWPATTERNRECT: return "DRAWPATTERNRECT";
    case GETVECTORPENSIZE: return "GETVECTORPENSIZE";
    case GETVECTORBRUSHSIZE: return "GETVECTORBRUSHSIZE";
    case ENABLEDUPLEX: return "ENABLEDUPLEX";
    case GETSETPAPERBINS: return "GETSETPAPERBINS";
    case GETSETPRINTORIENT: return "GETSETPRINTORIENT";
    case ENUMPAPERBINS: return "ENUMPAPERBINS";
    case SETDIBSCALING: return "SETDIBSCALING";
    case EPSPRINTING: return "EPSPRINTING";
    case ENUMPAPERMETRICS: return "ENUMPAPERMETRICS";
    case GETSETPAPERMETRICS: return "GETSETPAPERMETRICS";
    case POSTSCRIPT_DATA: return "POSTSCRIPT_DATA";
    case POSTSCRIPT_IGNORE: return "POSTSCRIPT_IGNORE";
    case GETDEVICEUNITS: return "GETDEVICEUNITS";
    case GETEXTENDEDTEXTMETRICS: return "GETEXTENDEDTEXTMETRICS";
    case GETPAIRKERNTABLE: return "GETPAIRKERNTABLE";
    case EXTTEXTOUT: return "EXTTEXTOUT";
    case GETFACENAME: return "GETFACENAME";
    case DOWNLOADFACE: return "DOWNLOADFACE";
    case METAFILE_DRIVER: return "METAFILE_DRIVER";
    case QUERYDIBSUPPORT: return "QUERYDIBSUPPORT";
    case BEGIN_PATH: return "BEGIN_PATH";
    case CLIP_TO_PATH: return "CLIP_TO_PATH";
    case END_PATH: return "END_PATH";
    case OPEN_CHANNEL: return "OPEN_CHANNEL";
    case DOWNLOADHEADER: return "DOWNLOADHEADER";
    case CLOSE_CHANNEL: return "CLOSE_CHANNEL";
    case POSTSCRIPT_PASSTHROUGH: return "POSTSCRIPT_PASSTHROUGH";
    case ENCAPSULATED_POSTSCRIPT: return "ENCAPSULATED_POSTSCRIPT";
    case POSTSCRIPT_IDENTIFY: return "POSTSCRIPT_IDENTIFY";
    case POSTSCRIPT_INJECTION: return "POSTSCRIPT_INJECTION";
    case CHECKJPEGFORMAT: return "CHECKJPEGFORMAT";
    case CHECKPNGFORMAT: return "CHECKPNGFORMAT";
    case GET_PS_FEATURESETTING: return "GET_PS_FEATURESETTING";
    case MXDC_ESCAPE: return "MXDC_ESCAPE";
    case SPCLPASSTHROUGH2: return "SPCLPASSTHROUGH2";
  }
return "";
}


void TconvertedPass1_WMF::wmf_ESCAPE(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::wmf_ESCAPE() ");fflush(log);
#endif
//static const uint16_t MathType[] = {15,12,'M'+256*'a','t'+256*'h','T'+256*'y','p'+256*'e',0};
//int Size;
uint16_t EscType;

  //CrackObject(this, ftell(wpd)+2*WmfRec.Size);

  Rd_word(wpd,&EscType);

  if(EscType==META_ESCAPE_ENHANCED_METAFILE)
  {
    uint32_t CommentId, CommentType;
    uint16_t ByteCount;
    Rd_word(wpd,&ByteCount);
    Rd_dword(wpd,&CommentId);
    if(CommentId==0x43464D57)	// A 32-bit uint that defines this record as a WMF Comment record. This value MUST be 0x43464D57.
    {
      uint16_t Checksum;
      uint32_t CommentType, Version;
      Rd_dword(wpd,&CommentType);
      Rd_dword(wpd,&Version);
      Rd_word(wpd,&Checksum);
    }
  }
//  if(WmfRec.Size-3<sizeof(MathType)/sizeof(uint16_t)) goto NoMTEF;

//NoMTEF:
  snprintf(ObjType, sizeof(ObjType),"!Escape %s", DecodeEscType(EscType));
}


/** This callback from parent allows to provide some generic functionality. */
int TconvertedPass1_WMF::Dispatch(int FuncNo, const void *arg)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::Dispatch(%d) ",FuncNo);fflush(log);
#endif

 switch(FuncNo)
   {
   case DISP_EXTRACTIMAGE:
         {
	 Image **FillME = (Image**)arg;
         if(FillME) *FillME = new Image(Img);
	 return 0;
	 }
   case DISP_NOCONVERTIMAGE:
         if(arg!=NULL)
         {
	   NoConvertImage = *(int*)arg;
           return 0;
         }
         break;
   }

return(-1);
}


/** Check whether WMF record has sufficient capacity.
 * @param[in]	WSizeRequest	Capacity of data payload in WORDs, without header record. */
bool TconvertedPass1_WMF::CheckWmfRecSz(uint16_t WSizeRequest)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::CheckWmfRecSz() ");fflush(log);
#endif
  if(WSizeRequest > WmfRec.Size-3)	// {RecordSize}[RecordFunction]
  {
    if(err != NULL)
        fprintf(err,_("\nError: WMF record %X size %d is too small, expected %u!"), 
                WmfRec.ParamFilePos ,WmfRec.Size ,WSizeRequest+2);
    return false;
  }
  return true;
}


void TconvertedPass1_WMF::parse_Arc(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Arc() ");fflush(log);
#endif
static const char ObjName[] = "!Arc";
int16_t BottomRect, TopRect, RightRect, LeftRect;
int16_t XRadial1, XRadial2, YRadial1, YRadial2;

  if(!CheckWmfRecSz(8))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&YRadial2);
  Rd_word(wpd, (uint16_t*)&XRadial2);
  Rd_word(wpd, (uint16_t*)&YRadial1);
  Rd_word(wpd, (uint16_t*)&XRadial1);

  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

  UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

  float Scale = GetScale2PSU((TMapMode)MapMode);
  VectorEllipse *pVecEllipse = new VectorEllipse(Scale*BottomRect, Scale*TopRect, Scale*RightRect, Scale*LeftRect);
  pVecEllipse->AttribFromPSS(PSS);
  pVecEllipse->BrushStyle = FILL_NONE;

  if(TopRect != BottomRect)
  {
    float xs = (LeftRect + RightRect) / 2;
    float ys = (BottomRect + TopRect) / 2;
    Scale = fabs((RightRect-LeftRect) / (float)(TopRect-BottomRect));

    pVecEllipse->bAngle = (180/M_PI) * atan2(Scale*(ys-YRadial1), XRadial1-xs);
    pVecEllipse->eAngle = (180/M_PI) * atan2(Scale*(ys-YRadial2), XRadial2-xs);
  }
  
  VectList.AddObject(pVecEllipse);

strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_Ellipse(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Ellipse() ");fflush(log);
#endif
static const char ObjName[] = "!Ellipse";
int16_t BottomRect, TopRect, RightRect, LeftRect;

  if(!CheckWmfRecSz(4))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

  UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

  const float Scale = GetScale2PSU((TMapMode)MapMode);

  VectorEllipse *pVecEllipse = new VectorEllipse(Scale*BottomRect, Scale*TopRect, Scale*RightRect, Scale*LeftRect);
  pVecEllipse->AttribFromPSS(PSS);
  
  VectList.AddObject(pVecEllipse);

strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_Chord(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Chord() ");fflush(log);
#endif
static const char ObjName[] = "!Chord";
int16_t	XRadial1, YRadial1, XRadial2, YRadial2;
int16_t	BottomRect, RightRect, TopRect, LeftRect;

  if(!CheckWmfRecSz(8))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&XRadial2);
  Rd_word(wpd, (uint16_t*)&YRadial2);
  Rd_word(wpd, (uint16_t*)&XRadial1);
  Rd_word(wpd, (uint16_t*)&YRadial1);
  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

strcpy(ObjType,ObjName+1);    // @TODO: dopsat!
}


void TconvertedPass1_WMF::parse_DibStretchBlt(void)
{
static const char ObjName[] = "!DibStretchBlt";
  //PleaseReport(ObjName+1);

  if(!CheckWmfRecSz(10))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Image *pImg = &Img;
	// Rewind to the last frame.
  while(pImg->Next!=NULL)
    pImg = pImg->Next;

  if(pImg->Raster==NULL || pImg->Raster->Data2D==NULL)
  {
    WMF_DIBSTRETCHBLT(*pImg, wpd, WmfRec.ParamFilePos);
    if(pImg->Raster!=NULL && pImg->Palette==NULL) pImg->AttachPalette(PSS.pPalette);
#ifdef _DEBUG
    //VectorRectangle *pVectRec = new VectorRectangle(pImg->y, pImg->y+pImg->dy, pImg->x, pImg->x+pImg->dx);
    //memset(&pVectRec->LineColor, 0x80, 3);
    //pVectRec->PenWidth = 2;
    //pVectRec->LineStyle = 1;
    //VectList.AddObject(pVectRec);
    //SavePictureEPS("o:\\temp\\22\\debug1.eps",*pImg);
#endif
  }
  else
  {
    Image Img2;
    WMF_DIBSTRETCHBLT(Img2, wpd, WmfRec.ParamFilePos);
    if(Img2.Raster!=NULL && Img2.Palette==NULL) Img2.AttachPalette(PSS.pPalette);
    //SavePictureEPS("o:\\temp\\22\\debug2.eps",Img2);

    if(Img2.Raster!=NULL && Img2.Raster->Size1D>0)
    {
      if(Img2.x == pImg->x && Img2.dx == pImg->dx &&		// The x size must match
         Img2.Raster->Size1D==pImg->Raster->Size1D && Img2.Raster->Size2D==1 && 
         Img2.Raster->GetPlanes()==pImg->Raster->GetPlanes())
      {
        void **NewBlock = (void**)realloc(pImg->Raster->Data2D, (pImg->Raster->Size2D+1)*sizeof(void**));
        if(NewBlock!=NULL)
        {
          NewBlock[pImg->Raster->Size2D] = Img2.Raster->Data2D[0];
          Img2.Raster->Data2D[0] = NULL;
	  // Img2.Raster->Size2D must not be schrinked here, schrink will cause leak. Dtor will free empty ptr.
          pImg->Raster->Data2D = NewBlock;
          pImg->Raster->Size2D++;
          pImg->dy = Img2.y - pImg->y + Img2.dy;
        }
      }
      else
      {
        pImg->Next = new Image;
        pImg = pImg->Next;
        pImg->AttachPalette(Img2.Palette);
        pImg->AttachRaster(Img2.Raster);
        pImg->x = Img2.x;        pImg->y = Img2.y;
        pImg->dx = Img2.dx;      pImg->dy = Img2.dy;
        pImg->RotAngle = Img2.RotAngle;
      }
    }
  }
strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_DibBitBlt(void)
{
static const char ObjName[] = "!DibBitBlt";
  //PleaseReport(ObjName+1);

  if(!CheckWmfRecSz(8))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Image *pImg = &Img;
	// Rewind to the last frame.
  while(pImg->Next!=NULL)
    pImg = pImg->Next;

  if(pImg->Raster==NULL || pImg->Raster->Data2D==NULL)
  {
    WMF_DIBBITBLT(*pImg, wpd, WmfRec.ParamFilePos);
    if(pImg->Raster!=NULL && pImg->Palette==NULL) pImg->AttachPalette(PSS.pPalette);
#ifdef _DEBUG
    //SavePictureEPS("o:\\temp\\22\\debug3.eps",*pImg);
    //pImg->AttachRaster(NULL);
#endif

  }
  else
  {
    Image Img2;
    WMF_DIBBITBLT(Img2, wpd, WmfRec.ParamFilePos);
    if(Img2.Raster!=NULL && Img2.Palette==NULL) Img2.AttachPalette(PSS.pPalette);
#ifdef _DEBUG
    //SavePictureEPS("o:\\temp\\22\\debug4.bmp",Img2);
#endif

    if(Img2.Raster!=NULL && Img2.Raster->Size1D>0)
    {
      if(Img2.x == pImg->x && Img2.dx == pImg->dx &&		// The x size must match
         Img2.Raster->Size1D==pImg->Raster->Size1D && Img2.Raster->Size2D==1 && 
         Img2.Raster->GetPlanes()==pImg->Raster->GetPlanes())
      {
        void **NewBlock = (void**)realloc(pImg->Raster->Data2D, (pImg->Raster->Size2D+1)*sizeof(void**));
        if(NewBlock!=NULL)
        {
          NewBlock[pImg->Raster->Size2D] = Img2.Raster->Data2D[0];
          Img2.Raster->Data2D[0] = NULL;
	  // Img2.Raster->Size2D must not be schrinked here, schrink will cause leak. Dtor will free empty ptr.
          pImg->Raster->Data2D = NewBlock;
          pImg->Raster->Size2D++;
          pImg->dy = Img2.y - pImg->y + Img2.dy;
        }
      }
      else
      {
        pImg->Next = new Image;
        pImg = pImg->Next;
        pImg->AttachPalette(Img2.Palette);
        pImg->AttachRaster(Img2.Raster);
        pImg->x = Img2.x;        pImg->y = Img2.y;
        pImg->dx = Img2.dx;      pImg->dy = Img2.dy;
        pImg->RotAngle = Img2.RotAngle;
      }
    }
  }
strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_FontIndirect(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_FontIndirect() ");fflush(log);
#endif
static const char ObjName[] = "!CreateFontIndirect";
int16_t Width, Height, Escapement;
uint8_t Underline, Strikeout, Charset, OutPrecision, ClipPrecision, Quality, PitchAndFamily;

  if(!CheckWmfRecSz(9))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&Height);
  Rd_word(wpd, (uint16_t*)&Width);

  Rd_word(wpd, (uint16_t*)&Escapement);		// Defines the angle, in tenths of degrees, between the escapement vector and the x-axis
						// of the device. The escapement vector is parallel to the base line of a row of text.
  Rd_word(wpd, (uint16_t*)&PSS.FontOrientation10);	// Defines the angle, in tenths of degrees, between each character's base line and the x-axis of the device.
  PSS.FontOrientation10 = -PSS.FontOrientation10;
  if(PSS.FontOrientation10==0 && Escapement!=0)
  {						// I do not understand too much how 'Escapement' differs from 'Orientation'.
    PSS.FontOrientation10 = Escapement;		// May be that 'Escapement' is signifficant when whole WMF block gets rotated.
  }

  Rd_word(wpd, &PSS.FontWeight);
  PSS.FontItallic = fgetc(wpd);
  Underline = fgetc(wpd);
  Strikeout = fgetc(wpd);

  Charset = fgetc(wpd);
  switch(Charset)
  {
    case OEM_CHARSET:
    case DEFAULT_CHARSET:	ConvertCpg = NULL;
				break;
    case ANSI_CHARSET:		ConvertCpg = GetTranslator("cp1252TOinternal");	break;
    case ARABIC_CHARSET:        ConvertCpg = GetTranslator("cp1256TOinternal"); break;
    case SYMBOL_CHARSET:	ConvertCpg = GetTranslator("symbolTOinternal");	break;
    case EASTEUROPE_CHARSET:	ConvertCpg = GetTranslator("cp1250TOinternal"); // see https://stackoverflow.com/questions/22911186/windows-encoding-clarification
				break;
    case RUSSIAN_CHARSET:	ConvertCpg = GetTranslator("cp1251TOinternal"); break;
    case MAC_CHARSET:		ConvertCpg = GetTranslator("MacRomanTOinternal"); break;
    case GREEK_CHARSET:		ConvertCpg = GetTranslator("cp1253TOinternal");	break;
    case HEBREW_CHARSET:	ConvertCpg = GetTranslator("cp1255TOinternal"); break;
    case TURKISH_CHARSET:	ConvertCpg = GetTranslator("cp1254TOinternal"); break;
    case SHIFTJIS_CHARSET:
    case HANGUL_CHARSET:
    case JOHAB_CHARSET:
    case GB2312_CHARSET:
    case CHINESEBIG5_CHARSET:
    case VIETNAMESE_CHARSET:
    case BALTIC_CHARSET:
    case THAI_CHARSET:

    default:			ConvertCpg = NULL; break;
  }
  
  if(ConvertCpg == NULL)
     {
     perc.Hide();
     fprintf(err, _("\nUnsupported charset: %Xh."), (unsigned)Charset);
     }

  PSS.ConvertCpg = ConvertCpg;

  OutPrecision = fgetc(wpd);
  ClipPrecision = fgetc(wpd);
  Quality = fgetc(wpd);
  PitchAndFamily = fgetc(wpd);

  snprintf(ObjType, sizeof(ObjType),"%s(H=%d,set=%u,rot=%d)", ObjName+1, (int)Height, (unsigned)Charset, PSS.FontOrientation10/10);

  if(YExtent<0) PSS.FontOrientation10 = -PSS.FontOrientation10;
  if(Height!=0)
  {
    if(Height>0)		// The font height is normally negative.
	PSS.FontOrientation10 = -PSS.FontOrientation10;
    PSS.FontSize = labs(Height)/2.66;
  }
  if(Width!=0) 
    PSS.FontSizeW = Width/2.66;		// [pt] --> [mm]
  else
    PSS.FontSizeW = PSS.FontSize;

  ObjTab.AddObject(new vecFont(PSS));
}


void TconvertedPass1_WMF::parse_MoveTo(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_MoveTo() ");fflush(log);
#endif
static const char ObjName[] = "!MoveTo";

  if(CheckWmfRecSz(2))
    {
    Rd_word(wpd, (uint16_t*)&PositionY);
    Rd_word(wpd, (uint16_t*)&PositionX);
    snprintf(ObjType, sizeof(ObjType),"%s(%d;%d)", ObjName+1, PositionX, PositionY);
    }
  else
    strcpy(ObjType,ObjName);  
}


void TconvertedPass1_WMF::parse_CreateBrushIndirect(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_CreateBrushIndirect() ");fflush(log);
#endif
static const char ObjName[] = "!CreateBrushIndirect";
uint16_t BrushStyle, HatchStyle;
  if(!CheckWmfRecSz(3))
    {
      ReportCorruptedObj(ObjName+1);
MemoryError:
      strcpy(ObjType, ObjName);
      return;
    }

  vecBrush *pVB = new vecBrush(PSS);
  if(pVB==NULL) goto MemoryError;

  Rd_word(wpd, &BrushStyle);
  pVB->FillColor.Red   = fgetc(wpd);
  pVB->FillColor.Green = fgetc(wpd);
  pVB->FillColor.Blue  = fgetc(wpd);  
  fseek(wpd,1,SEEK_CUR);
  Rd_word(wpd, &HatchStyle);

  switch(BrushStyle)
  {
    case BS_SOLID:    pVB->BrushStyle=FILL_SOLID; break;
    case BS_HATCHED:  {		      
		      if(labs(YExtent)>5000 || labs(XExtent)>5000)
		      {
		        if(GetScale2PSU((TMapMode)MapMode) == 1)
			{
			  PSS.HatchDistance = MAX(labs(YExtent),labs(XExtent)) / 500.0f;		          
			}
		      }
		      switch(HatchStyle)
		      {
		        case HS_HORIZONTAL:	pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_HORIZONTAL2 : FILL_HORIZONTALx; break;
		        case HS_VERTICAL:	pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_VERTICAL4 : FILL_VERTICALx; break;
                        case HS_FDIAGONAL:	pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_DIAG_DOWN2 : FILL_DIAG_DOWNx; break;
                        case HS_BDIAGONAL:	pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_DIAG_UP4 : FILL_DIAG_UPx; break;
                        case HS_CROSS:		pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_CROSS_HATCH2: FILL_CROSS_HATCHx; break;
                        case HS_DIAGCROSS:	pVB->BrushStyle=(PSS.HatchDistance==1) ? FILL_DIAG_CROSSHATCH4 : FILL_DIAG_CROSSHATCHx; break;
			default: pVB->BrushStyle=FILL_DIAG_UP; break;
		      }
		      if(MixMode==2 && pVB->BrushStyle>FILL_SOLID)
			pVB->BrushStyle|=0x80;
		      }
		      break;
    case BS_NULL:
    default:	      pVB->BrushStyle=FILL_NONE; break;
  }

  ObjTab.AddObject(pVB);
  snprintf(ObjType, sizeof(ObjType), "%s(sty=%u;RGB=%2.2X,%2.2X,%2.2X)", ObjName+1, BrushStyle, pVB->FillColor.Red, pVB->FillColor.Green, pVB->FillColor.Blue);
  //printf(" %s(sty=%u;RGB=%2.2X,%2.2X,%2.2X)", ObjName+1, BrushStyle, pVB->FillColor.Red, pVB->FillColor.Green, pVB->FillColor.Blue);
}


void TconvertedPass1_WMF::parse_CreatePalette(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_CreatePalette() ");fflush(log);
#endif
static const char ObjName[] = "!CreatePalette";

  if(!CheckWmfRecSz(2))
    {
RecordCorrupted:
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType, ObjName);
    return;
    }

  uint16_t NumberOfEntries;
  Rd_word(wpd, &NumberOfEntries);	// Start MUST be 0x0300.
  if(NumberOfEntries!=0x300) goto RecordCorrupted;
  Rd_word(wpd, &NumberOfEntries);

  PSS.AttachPalette(NULL);

  if(!CheckWmfRecSz(2+2*NumberOfEntries)) goto RecordCorrupted;

  PSS.pPalette = BuildPalette(NumberOfEntries,8);
  if(PSS.pPalette==NULL) goto RecordCorrupted;
  PSS.pPalette->UsageCount = 1;

  for(unsigned i=0; i<NumberOfEntries; i++)
  {
    uint32_t d;
    RGBQuad RGB;

    Rd_dword(wpd, &d);
    RGB.R = d & 0xFF;
    RGB.G = (d >> 8) & 0xFF;
    RGB.B = (d >> 8) & 0xFF;
    PSS.pPalette->Set(i,&RGB);
  }

  ObjTab.AddObject(new attrPalette(PSS.pPalette));
  snprintf(ObjType, sizeof(ObjType), "%s(n=%u)", ObjName+1, NumberOfEntries);
}


void TconvertedPass1_WMF::parse_DibCreatePatternBrush(void)
{
static const char ObjName[] = "!DibCreatePatternBrush";

  if(!CheckWmfRecSz(3))
    {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType, ObjName);
    ObjTab.AddObject(new FakeAttr());
    return;
    }

  uint16_t Style;
  uint16_t ColorUsage;

  Rd_word(wpd, &Style);
  Rd_word(wpd, &ColorUsage);
  
  Image Img;
  LoadBmpStream(Img, wpd, WmfRec.ParamFilePos+4);
  if(Img.Raster != NULL)
  {
    ObjTab.AddObject(new attrRaster(Img.Raster,Img.Palette));
    strcpy(ObjType, ObjName+1);
  }
  else
  {
    ObjTab.AddObject(new FakeAttr());
    strcpy(ObjType, ObjName);
  }
}


void TconvertedPass1_WMF::parse_CreatePenIndirect(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_CreatePenIndirect() ");fflush(log);
#endif
static const char ObjName[] = "!CreatePenIndirect";
uint16_t PenStyle, PenWidthX, PenWidthY;
  if(!CheckWmfRecSz(5))
    {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType, ObjName);
    return;
    }

  Rd_word(wpd, &PenStyle);
  switch(PenStyle & 0xFF)
  {    
    case PS_DASH:	PSS.LineStyle=7; break;// 0x0001, 
    case PS_DOT:	PSS.LineStyle=10; break;// 0x0002,
    case PS_DASHDOT:	PSS.LineStyle=15; break;// 0x0003,
    case PS_DASHDOTDOT: PSS.LineStyle=19; break;// 0x0004, 
    case PS_NULL:	PSS.LineStyle=0; break;	// 0x0005, 
    //case PS_INSIDEFRAME = 0x0006, 
    //case PS_USERSTYLE = 0x0007, 
    //case PS_ALTERNATE = 0x0008,
    case PS_SOLID:	
    default:
			PSS.LineStyle=1; break;	// 0x0000, 
  }
  PenWidthX = PenStyle & 0x0F00;
  switch(PenWidthX)
  {
    case PS_ENDCAP_ROUND:	PSS.LineCap=1; break;	// 0x0000
    case PS_ENDCAP_SQUARE:	PSS.LineCap=2; break;	// 0x0100
    case PS_ENDCAP_FLAT:	PSS.LineCap=0; break;	// 0x0200
  }
  PenWidthX = PenStyle & 0xF000;
  switch(PenWidthX)
  {
    case PS_JOIN_ROUND:		PSS.LineJoin=1; break;	// 0x0000 1 (round join)
    case PS_JOIN_BEVEL:		PSS.LineJoin=2; break;	// 0x1000 2 (bevel join)
    case PS_JOIN_MITER:		PSS.LineJoin=0; break;	// 0x2000 0 (miter join)
  }

  Rd_word(wpd, &PenWidthX);
  Rd_word(wpd, &PenWidthY);
  PSS.LineWidth = GetScale2PSU((TMapMode)MapMode) * ((PenWidthX>PenWidthY) ? PenWidthX : PenWidthY);

  PSS.LineColor.Red   = fgetc(wpd);
  PSS.LineColor.Green = fgetc(wpd);
  PSS.LineColor.Blue  = fgetc(wpd);
  
  ObjTab.AddObject(new vecPen(PSS));
  snprintf(ObjType, sizeof(ObjType), "%s(sty=%d,W=%u;RGB=%2.2X,%2.2X,%2.2X)", ObjName+1, PenStyle, PenWidthX,
      PSS.LineColor.Red, PSS.LineColor.Green, PSS.LineColor.Blue);
}


void TconvertedPass1_WMF::parse_Rectangle(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Rectangle() ");fflush(log);
#endif
static const char ObjName[] = "!Rectangle";
int16_t	BottomRect, TopRect, RightRect, LeftRect;

  if(!CheckWmfRecSz(4))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

  UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

  const float Scale = GetScale2PSU((TMapMode)MapMode);

  VectorRectangle *pVectRec = new VectorRectangle(Scale*BottomRect, Scale*TopRect, Scale*LeftRect, Scale*RightRect);
  pVectRec->AttribFromPSS(PSS);
  VectList.AddObject(pVectRec);

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_SetPixel(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetPixel() ");fflush(log);
#endif
static const char ObjName[] = "!SetPixel";
int16_t x,y;
RGB_Record PixColor;

  if(!CheckWmfRecSz(4))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
	
  PixColor.Red = fgetc(wpd);
  PixColor.Green = fgetc(wpd);
  PixColor.Blue = fgetc(wpd);
  fseek(wpd, 1, SEEK_CUR);
  Rd_word(wpd, (uint16_t*)&y);
  Rd_word(wpd, (uint16_t*)&x);

  UpdateBBox(bbx, 0, x-0.5, y-0.5, 1, 1);

  const float Scale = GetScale2PSU((TMapMode)MapMode);

  VectorRectangle *pVectRec = new VectorRectangle(Scale*(y-0.5f), Scale*(y+0.5f), Scale*(x-0.5f), Scale*(x+0.5f));
  pVectRec->AttribFromPSS(PSS);
  pVectRec->FillColor = PixColor;
  pVectRec->BrushStyle = FILL_SOLID;
  pVectRec->LineStyle = 0;
  VectList.AddObject(pVectRec);  

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_RoundRect(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_RoundRect() ");fflush(log);
#endif
static const char ObjName[] = "!RoundRect";
int16_t BottomRect, TopRect, RightRect, LeftRect;
int16_t Height, Width;

  if(!CheckWmfRecSz(6))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&Height);
  Rd_word(wpd, (uint16_t*)&Width);
  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

  UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

  const float Scale = GetScale2PSU((TMapMode)MapMode);

  VectorRectangle *pVectRec = new VectorRectangleArc(Scale*BottomRect, Scale*TopRect, Scale*LeftRect, Scale*RightRect, 
						     Scale*Width/2.0f, Scale*Height/2.0f);
  pVectRec->AttribFromPSS(PSS);
  VectList.AddObject(pVectRec);  

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_DeleteObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_DeleteObject() ");fflush(log);
#endif
uint16_t ObjectIndex;
static const char ObjName[] = "!DeleteObject";

  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_word(wpd, (uint16_t*)&ObjectIndex);
  ObjTab.DeleteObject(ObjectIndex);
  snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);  
}


void TconvertedPass1_WMF::parse_SelectObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SelectObject() ");fflush(log);
#endif
uint16_t ObjectIndex;
static const char ObjName[] = "!SelectObject";

  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_word(wpd, &ObjectIndex);

  const VectorAttribute *vecAttr = ObjTab.GetObjectAt(ObjectIndex);
  if(vecAttr==NULL)
  {
    strcpy(ObjType,ObjName);
    if(err!=NULL)
    {
      perc.Hide();
      fprintf(err,_("\nError: cannot select WMF object on position %d."), ObjectIndex);
    }
    return;
  }

  //printf("\n%s(%u) ", ObjName+1, ObjectIndex);
  vecAttr->prepExport(&PSS);
  ConvertCpg = PSS.ConvertCpg;
  snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);  
}


void TconvertedPass1_WMF::parse_SelectPalette(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SelectPalette() ");fflush(log);
#endif
uint16_t ObjectIndex;
static const char ObjName[] = "!SelectPalette";

  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_word(wpd, &ObjectIndex);

  const VectorAttribute *vecAttr = ObjTab.GetObjectAt(ObjectIndex);
  if(vecAttr==NULL)
  {
OBJECT_DEFFECT:
    snprintf(ObjType, sizeof(ObjType),"%s(%u)",ObjName,ObjectIndex);
    if(err!=NULL)
    {
      perc.Hide();
      fprintf(err,_("\nError: cannot select WMF object on position %d."), ObjectIndex);
    }
    return;
  }
  if(vecAttr->getAttribType()!=ATTR_PALETTE) goto OBJECT_DEFFECT;

  vecAttr->prepExport(&PSS);
  ConvertCpg = PSS.ConvertCpg;
  snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);  
}


void TconvertedPass1_WMF::parse_SetBkColor(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetBkColor() ");fflush(log);
#endif
  if(CheckWmfRecSz(2))
    {
    PSS.FillBackground.Red   = fgetc(wpd);
    PSS.FillBackground.Green = fgetc(wpd);
    PSS.FillBackground.Blue  = fgetc(wpd);
    }

  snprintf(ObjType, sizeof(ObjType),"SetBkColor(%2.2X,%2.2X,%2.2X)",PSS.FillBackground.Red,PSS.FillBackground.Green,PSS.FillBackground.Blue);
}


void TconvertedPass1_WMF::parse_Pie(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Pie() ");fflush(log);
#endif
static const char ObjName[] = "!Pie";
int16_t BottomRect, TopRect, RightRect, LeftRect;
int16_t XRadial1, XRadial2, YRadial1, YRadial2;

  if(!CheckWmfRecSz(8))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, (uint16_t*)&YRadial2);
  Rd_word(wpd, (uint16_t*)&XRadial2);
  Rd_word(wpd, (uint16_t*)&YRadial1);
  Rd_word(wpd, (uint16_t*)&XRadial1);

  Rd_word(wpd, (uint16_t*)&BottomRect);
  Rd_word(wpd, (uint16_t*)&RightRect);
  Rd_word(wpd, (uint16_t*)&TopRect);
  Rd_word(wpd, (uint16_t*)&LeftRect);

  UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

  float Scale = GetScale2PSU((TMapMode)MapMode);
  VectorEllipse *pVecEllipse = new VectorPie(Scale*BottomRect, Scale*TopRect, Scale*RightRect, Scale*LeftRect);
  pVecEllipse->AttribFromPSS(PSS);

  if(TopRect != BottomRect)
  {
    float xs = (LeftRect + RightRect) / 2;
    float ys = (BottomRect + TopRect) / 2;
    Scale = fabs((RightRect-LeftRect) / (float)(TopRect-BottomRect));

    pVecEllipse->bAngle = (180/M_PI) * atan2(Scale*(ys-YRadial1), XRadial1-xs);
    pVecEllipse->eAngle = (180/M_PI) * atan2(Scale*(ys-YRadial2), XRadial2-xs);
  }
  
  VectList.AddObject(pVecEllipse);

strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_SetTextColor(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetTextColor() ");fflush(log);
#endif
  if(CheckWmfRecSz(2))
    {
    PSS.TextColor.Red   = fgetc(wpd);
    PSS.TextColor.Green = fgetc(wpd);
    PSS.TextColor.Blue  = fgetc(wpd);
    }

  strcpy(ObjType,"SetTextColor");
}


void TconvertedPass1_WMF::parse_SaveDC(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SaveDC() ");fflush(log);
#endif
static const char ObjName[] = "!SaveDC";

  PushDC();
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_RestoreDC(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_RestoreDC() ");fflush(log);
#endif
static const char ObjName[] = "!RestoreDC";

  PopDC();
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_SetWindowExt(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetWindowExt() ");fflush(log);
#endif
  Rd_word(wpd, (uint16_t*)&YExtent);
  Rd_word(wpd, (uint16_t*)&XExtent);
  snprintf(ObjType, sizeof(ObjType),"SetWindowExt(%d,%d)", (int)XExtent, (int)YExtent);
}


void TconvertedPass1_WMF::parse_SetWindowOrg(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetWindowOrg() ");fflush(log);
#endif
  Rd_word(wpd, (uint16_t*)&YOffset);
  Rd_word(wpd, (uint16_t*)&XOffset);
  snprintf(ObjType, sizeof(ObjType),"SetWindowOrg(%d,%d)", (int)XOffset, (int)YOffset);
}


void TconvertedPass1_WMF::parse_LineTo(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_LineTo() ");fflush(log);
#endif
static const char ObjName[] = "!LineTo";

  if(CheckWmfRecSz(2))
  {
    int16_t LineX, LineY;

    Rd_word(wpd, (uint16_t*)&LineY);	// Y is first
    Rd_word(wpd, (uint16_t*)&LineX);

    UpdateBBox(bbx, 0, PositionX, PositionY, LineX-PositionX, LineY-PositionY);

    float *Points = (float*)malloc(4*sizeof(float));
    if(Points!=NULL)
    {
      const float Scale = GetScale2PSU((TMapMode)MapMode);

      Points[0] = PositionX * Scale;
      Points[1] = PositionY * Scale;
      PositionX = LineX;		// Update position.
      PositionY = LineY;
      Points[2] = LineX * Scale;
      Points[3] = LineY * Scale;
      VectorLine *pVecLine = new VectorLine(Points, 2);

      memcpy(&pVecLine->LineColor, &PSS.LineColor, sizeof(PSS.LineColor));
      pVecLine->LineStyle = PSS.LineStyle;
      pVecLine->LineCap = PSS.LineCap;
      pVecLine->PenWidth = PSS.LineWidth;
      pVecLine->Close = false;
      VectList.AddObject(pVecLine); pVecLine=NULL;

      snprintf(ObjType, sizeof(ObjType),"%s(%d;%d)", ObjName+1, LineX, LineY);
      return;
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_WMF::parse_Polygon(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Polygon() ");fflush(log);
#endif
static const char ObjName[] = "!Polygon";
  uint16_t PolySize;  

  if(!CheckWmfRecSz(1))
  {
POLY_FAIL:
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, &PolySize);
  if(PolySize > (WmfRec.Size - 3 - 1)/2)
    PolySize = (WmfRec.Size - 3 - 1)/2;
  float *Points = LoadPoints(this, PolySize);
  if(Points==NULL)  goto POLY_FAIL;
  FixBoundingBox(Points, PolySize, bbx);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  for(int i=0; i<2*PolySize; i++)
  {
    Points[i] *= Scale;		// scale both x & y
  }

  VectorPolygon *pVecPoly = new VectorPolygon(Points, PolySize);
  if(pVecPoly==NULL) {free(Points);goto POLY_FAIL;}
  Points = NULL;
  pVecPoly->AttribFromPSS(PSS);  
  pVecPoly->Close = true;
  VectList.AddObject(pVecPoly); pVecPoly=NULL;

  strcpy(ObjType,ObjName+1);
  return;
}


void TconvertedPass1_WMF::parse_PolyPolygon(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_PolyPolygon() ");fflush(log);
#endif
static const char ObjName[] = "!PolyPolygon";
uint16_t PolyCount;
uint16_t *PolySizes;
uint16_t i;

  if(!CheckWmfRecSz(1+1))
  {
POLY_FAIL:
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, &PolyCount);
  if(PolyCount<=0) goto POLY_FAIL;

  PolySizes = (uint16_t*)malloc(PolyCount*sizeof(uint16_t));
  if(PolySizes==NULL) goto POLY_FAIL;
  for(i=0; i<PolyCount;i++)
  {
    Rd_word(wpd, &PolySizes[i]);
  }

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  for(i=0; i<PolyCount; i++)
  {
    if(PolySizes[i]<=0) continue;
    //if(PolySize > (WmfRec.Size - 3 - 1)/2)
    //  PolySize = (WmfRec.Size - 3 - 1)/2;
    float *Points = LoadPoints(this, PolySizes[i]);
    if(Points==NULL)
        {free(PolySizes);goto POLY_FAIL;}
    FixBoundingBox(Points, PolySizes[i], bbx);
  
    for(int j=0; j<2*PolySizes[i]; j++)
    {
      Points[j] *= Scale;		// scale both x & y
    }
    VectorPolygon *pVecPoly = new VectorPolygon(Points, PolySizes[i]);
    Points = NULL;
    pVecPoly->AttribFromPSS(PSS);    
    //memcpy(&pVecPoly->FillColor, &PSS.FillColor, sizeof(PSS.FillColor));
    //pVecPoly->BrushStyle = PSS.FillPattern;  
    /*if(i==0)		// debugging only!
      {
      pVecPoly->BrushStyle = 0; //PSS.FillPattern;  
      pVecPoly->LineStyle = 1;
      memset(&pVecPoly->LineColor, 0, sizeof(pVecPoly->LineColor));
      pVecPoly->PenWidth = 10*GetScale2PSU((TMapMode)MapMode);
      } */

    pVecPoly->Close = true;
    VectList.AddObject(pVecPoly); pVecPoly=NULL;
  }
  free(PolySizes);
  strcpy(ObjType,ObjName+1);  
  return;
}


void TconvertedPass1_WMF::parse_PolyLine(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_PolyLine() ");fflush(log);
#endif
static const char ObjName[] = "!Polyline";
  uint16_t PolySize;

  if(!CheckWmfRecSz(1))
  {
POLY_FAIL:
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, &PolySize);
  if(PolySize > (WmfRec.Size - 3 - 1)/2)
    PolySize = (WmfRec.Size - 3 - 1)/2;  
  float *Points = LoadPoints(this, PolySize);
  if(Points==NULL)  goto POLY_FAIL;
  FixBoundingBox(Points, PolySize, bbx);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  for(int i=0; i<2*PolySize; i++)
  {
    Points[i] *= Scale;		// scale both x & y
  }

  VectorLine *pVecLine = new VectorLine(Points, PolySize);
  Points = NULL;
  pVecLine->AttribFromPSS(PSS);  
  pVecLine->Close = false;
  VectList.AddObject(pVecLine); pVecLine=NULL;

  strcpy(ObjType,ObjName+1);  
}


void TconvertedPass1_WMF::parse_SetBkMode(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetBkMode() ");fflush(log);
#endif
static const char ObjName[] = "!SetBkMode(%u)";
  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,9);
    return;
  }
  uint16_t BkMode;
  Rd_word(wpd, &BkMode);

  if(BkMode==2)		// Opaque
  {
    MixMode = 2;
    if(PSS.FillPattern>FILL_SOLID) PSS.FillPattern |= 0x80;    
  }
  else			// Transparent
  {
    MixMode = 1;
    PSS.FillPattern&=~0x80;
  }
  snprintf(ObjType, sizeof(ObjType),ObjName+1,(unsigned)BkMode);
}


void TconvertedPass1_WMF::parse_SetMapMode(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetMapMode() ");fflush(log);
#endif
static const char ObjName[] = "!SetMapMode(%u)";
  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,11);
    return;
  }
  Rd_word(wpd, &MapMode);
  snprintf(ObjType, sizeof(ObjType),ObjName+1,(unsigned)MapMode);
}


void TconvertedPass1_WMF::parse_SetPolyFillMode(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetPolyFillMode() ");fflush(log);
#endif
static const char ObjName[] = "!SetPolyFillMode";
uint16_t PolyFillMode;
  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,16);
    return;
  }  
  Rd_word(wpd, &PolyFillMode);		// ALTERNATE=0x0001, WINDING=0x0002
  PSS.PolyFillMode = (PolyFillMode==1) ? 1 : 0;
  snprintf(ObjType, sizeof(ObjType), "%s(%u)", ObjName+1, (unsigned)PolyFillMode);
}


void TconvertedPass1_WMF::parse_SetTextAlign(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetTextAlign() ");fflush(log);
#endif
uint16_t TextAlignmentMode;
static const char ObjName[] = "!SetTextAlign";

  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_word(wpd, &TextAlignmentMode);

  PSS.TextAlign = TextAlignmentMode & 0xFF;
 
  snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName+1, TextAlignmentMode);
}


void TconvertedPass1_WMF::parse_TextOut(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_TextOut() ");fflush(log);
#endif
static const char ObjName[] = "!TextOut";
uint16_t StringLen;
short int LineLen, CurLineLen;
int16_t XStart;

  if(!CheckWmfRecSz(3))
  {
TEXT_OUT_FAIL:
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_word(wpd, &StringLen);
  if(!CheckWmfRecSz((StringLen+1)/2)) goto TEXT_OUT_FAIL;
  
  const float Scale = GetScale2PSU((TMapMode)MapMode);  
  TextContainer *pTextCont = new TextContainer;  
  pTextCont->PosX = 0;
  pTextCont->PosY = 0;
  pTextCont->TextAlign = PSS.TextAlign;
  pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
  pTextCont->MixMode = MixMode;
  LineLen = CurLineLen = 0;
  XStart = StringLen & 1;
  while(StringLen-->0)
  {
    char ch = fgetc(wpd);
    if(ch==0)
    {
      fseek(wpd,StringLen,SEEK_CUR);
      break;
    }

    if(ch=='\n')
    {
      if(CurLineLen>LineLen) LineLen=CurLineLen;
      if(!pTextCont->isEmpty())
      {
        const float CurPosY = pTextCont->PosY + mm2PSu(PSS.FontSize);
        VectList.AddObject(pTextCont);
        pTextCont = new TextContainer;
        pTextCont->PosX = 0;
        pTextCont->PosY = CurPosY;
        pTextCont->TextAlign = PSS.TextAlign;
        pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
        pTextCont->MixMode = MixMode;
      }
    }
    else CurLineLen++;

    if(ConvertCpg != NULL)
    {
      if(!strcmp(ConvertCpg->Name,"symbolTOinternal"))      
      {
        pTextCont->AddText(ch, "Symbol", PSS);		// Pass symbols directly.
      }
      else
      {        
        AddCharacterToContainer(pTextCont, (*ConvertCpg)[ch], PsNativeCP, PsNativeSym, this, PSS);
      }
    }
    else
    {      
      pTextCont->AddText(ch,PSS);
    }
  }
  if(CurLineLen>LineLen) LineLen=CurLineLen;
  if(XStart>0)			// Word alignment.
    fseek(wpd,1,SEEK_CUR);

  int16_t YStart;
  Rd_word(wpd, (uint16_t*)&YStart);
  Rd_word(wpd, (uint16_t*)&XStart);

  pTextCont->PosX = XStart*Scale;
  pTextCont->PosY += YStart*Scale;

  if(pTextCont->isEmpty())    
    delete pTextCont;    
  else    
    VectList.AddObject(pTextCont);    
  pTextCont=NULL;  

  UpdateBBox(bbx, 0, XStart, YStart + 0.1*mm2PSu(PSS.FontSize), 
             LineLen*mm2PSu(PSS.FontSizeW), -1.1*mm2PSu(PSS.FontSize));

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_WMF::parse_ExtTextOut(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_ExtTextOut() ");fflush(log);
#endif
static const char ObjName[] = "!ExtTextOut";
int16_t x_pos, y_pos;
uint16_t fuOptions, TextLen;
short int LineLen, CurLineLen;
unsigned char c;

  if(!CheckWmfRecSz(4))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_word(wpd, (uint16_t*)&y_pos);
  Rd_word(wpd, (uint16_t*)&x_pos);
  Rd_word(wpd, &TextLen);  
  Rd_word(wpd, &fuOptions);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  int MaxTextLen = 2*(WmfRec.Size - 4);
  if(fuOptions & 0x0004)		// ETO_CLIPPED Rectangle provided
  {        
    fseek(wpd,8,SEEK_CUR);
    MaxTextLen -= 8;
  }
#ifdef _DEBUG
/*  else
  {
    const float sz = mm2PSu(PSS.FontSize)/2;
    VectorRectangle *pVectRec = new VectorRectangle(Scale*y_pos-sz, Scale*y_pos+sz, Scale*x_pos-sz, Scale*x_pos+sz);
    pVectRec->AttribFromPSS(PSS);    
    VectList.AddObject(pVectRec);
  } */
#endif

  if(MaxTextLen < TextLen)
  {
    if(err!=NULL)
    {
      perc.Hide();
      fprintf(err, _("\nError: text with size %d cannot fit into object, cut to %u."), MaxTextLen, TextLen);
    }
    TextLen = MaxTextLen;
  }

  TextContainer *pTextCont = new TextContainer;  
  pTextCont->PosX = Scale*x_pos;
  pTextCont->PosY = Scale*y_pos;
  pTextCont->TextAlign = PSS.TextAlign;
  pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
  pTextCont->MixMode = MixMode;

  LineLen = CurLineLen = 0;
  if(log!=NULL) fputc(' ',log);
  while(!feof(wpd))
    {
    if(TextLen-- <= 0) break;
    c = fgetc(wpd);
    if(c==0) break;

    if(log!=NULL) fputc(c,log);
    if(c=='\n')
    {
      if(CurLineLen>LineLen) LineLen=CurLineLen;
      if(!pTextCont->isEmpty())
      {
        const float CurPosY = pTextCont->PosY + mm2PSu(PSS.FontSize);
        VectList.AddObject(pTextCont);
        pTextCont = new TextContainer;        
        pTextCont->PosX = Scale*x_pos;        
        pTextCont->PosY = CurPosY;
	pTextCont->TextAlign = PSS.TextAlign;
        pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
        pTextCont->MixMode = MixMode;
      }
    }
    else CurLineLen++;

    if(ConvertCpg != NULL)
      {
      if(!strcmp(ConvertCpg->Name,"symbolTOinternal"))
        pTextCont->AddText((char)c, "Symbol", PSS);	// Push Symbol code page without any change.
      else
        {
        AddCharacterToContainer(pTextCont, (*ConvertCpg)[c], PsNativeCP, PsNativeSym, this, PSS);
        }
      }
    else
      {
      pTextCont->AddText((char)c,PSS);
      }
    }

  if(CurLineLen>LineLen) LineLen=CurLineLen;

  if(pTextCont->isEmpty())
    delete pTextCont;
  else
    {
    if((PSS.TextAlign & 6) == 6)	// TA_CENTER
      {      
      x_pos -= LineLen*mm2PSu(PSS.FontSize) / 4;
      }
    VectList.AddObject(pTextCont);    
    }
  pTextCont=NULL;

  //if(VectList.isEmpty()) goto TEXT_OUT_FAIL;  

  UpdateBBox(bbx, 0, x_pos, y_pos+0.1*mm2PSu(PSS.FontSize),		// Extend down reserved space for character.
             LineLen*mm2PSu(PSS.FontSizeW)/2.0, -1.1*mm2PSu(PSS.FontSize));

  strcpy(ObjType,ObjName+1);  
}


void TconvertedPass1_WMF::StretchDIBits(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::StretchDIBits() ");fflush(log);
#endif
Image *pImg = &Img;

	// Rewind to the last frame.
  while(pImg->Next!=NULL)
    pImg = pImg->Next;

  if(pImg->Raster==NULL || pImg->Raster->Data2D==NULL)
  {
    WMF_STRETCHDIB(*pImg, wpd, WmfRec.ParamFilePos);
    if(pImg->Raster!=NULL && pImg->Palette==NULL) pImg->AttachPalette(PSS.pPalette);
  }
  else
  {
    Image Img2;
    WMF_STRETCHDIB(Img2, wpd, WmfRec.ParamFilePos);
    if(Img2.Raster!=NULL && Img2.Palette==NULL) Img2.AttachPalette(PSS.pPalette);
    //SavePictureEPS("R:\\4\\debug.eps",Img2);

    if(Img2.Raster!=NULL && Img2.Raster->Size1D>0)
    {
      if(Img2.x == pImg->x && Img2.dx == pImg->dx &&		// The x size must match
         Img2.Raster->Size1D==pImg->Raster->Size1D && Img2.Raster->Size2D==1 && 
         Img2.Raster->GetPlanes()==pImg->Raster->GetPlanes())
      {
        void **NewBlock = (void**)realloc(pImg->Raster->Data2D, (pImg->Raster->Size2D+1)*sizeof(void**));
        if(NewBlock!=NULL)
        {
          NewBlock[pImg->Raster->Size2D] = Img2.Raster->Data2D[0];
          Img2.Raster->Data2D[0] = NULL;
	  // Img2.Raster->Size2D must not be schrinked here, schrink will cause leak. Dtor will free empty ptr.
          pImg->Raster->Data2D = NewBlock;
          pImg->Raster->Size2D++;
          pImg->dy = Img2.y - pImg->y + Img2.dy;
        }
      }
      else
      {
        pImg->Next = new Image;
        pImg = pImg->Next;
        pImg->AttachPalette(Img2.Palette);
        pImg->AttachRaster(Img2.Raster);
        pImg->x = Img2.x;        pImg->y = Img2.y;
        pImg->dx = Img2.dx;      pImg->dy = Img2.dy;
        pImg->RotAngle = Img2.RotAngle;
      }
    }
  }
  strcpy(ObjType,"StretchDIBits");
}


int TconvertedPass1_WMF::LoadImageWMF(void)
{
_WindowsMetaHeader WmfHead;
uint32_t NewPos;

  if(LoadWMFHeader(wpd,WmfHead)!=18) return(-1);
  
  if(WmfHead.HeaderSize!=9)
    {
    if(err != NULL)
        fprintf(err, _("\nError: Invalid WMF header size %u, expected size=9!"), WmfHead.HeaderSize);
    return(-1);
    }

  //perc.Init(ftell(wpd), fsize,_("First pass WMF:") );

  ActualPos = ftell(wpd);
  VectorList VectList;
  OutCodePage = 0;
  while(!feof(wpd))
    {    
    if(LoadWmfRecord(wpd,WmfRec))
      {
      *ObjType = 0;
      NewPos = 0;

      switch(WmfRec.Function)
	{
	case 0x0000: strcpy(ObjType,"WMF End"); break;		// META_EOF

	case 0x001E: parse_SaveDC(); break;			// META_SAVEDC

	case 0x0035: strcpy(ObjType,"!RealizePalette"); break;	// META_REALIZEPALETTE
	case 0x0037: strcpy(ObjType,"!SetPalEntries"); break;	// META_SETPALENTRIES

	case 0x004F: strcpy(ObjType,"!StartPage"); break;	//?
	case 0x0050: strcpy(ObjType,"!EndPage"); break;		//?

	case 0x0052: strcpy(ObjType,"!AbortDoc"); break;	//?
	case 0x005E: strcpy(ObjType,"!EndDoc"); break;		//?

	case 0x00F7: parse_CreatePalette(); break;		// META_CREATEPALETTE
	case 0x00F8: strcpy(ObjType,"!CreateBrush"); break;	//?

	case 0x0102: parse_SetBkMode(); break;			// META_SETBKMODE
	case 0x0103: parse_SetMapMode(); break;			// META_SETMAPMODE
	case 0x0104: strcpy(ObjType,"!SetROP2"); break;		// META_SETROP2
	case 0x0105: strcpy(ObjType,"!SetRelabs"); break;	// META_SETRELABS
	case 0x0106: parse_SetPolyFillMode(); break;		// META_SETPOLYFILLMODE
	case 0x0107: strcpy(ObjType,"!SetStretchBltMode"); break;// META_SETSTRETCHBLTMODE
	case 0x0108: strcpy(ObjType,"!SetTextCharExtra"); break; // META_SETTEXTCHAREXTRA

	case 0x0127: parse_RestoreDC(); break;			// META_RESTOREDC

	case 0x012A: strcpy(ObjType,"!InvertRegion"); PleaseReport(ObjType+1); break; // META_INVERTREGION
	case 0x012B: strcpy(ObjType,"!PaintRegion"); PleaseReport(ObjType+1); break; // META_PAINTREGION
	case 0x012C: strcpy(ObjType,"!SelectClipRegion"); break;// META_SELECTCLIPREGION
	case 0x012D: parse_SelectObject(); break;		// META_SELECTOBJECT
	case 0x012E: parse_SetTextAlign(); break;		// META_SETTEXTALIGN

	case 0x0139: strcpy(ObjType,"!ResizePalette"); break;	// META_RESIZEPALETTE

	case 0x0142: parse_DibCreatePatternBrush(); break;	// META_DIBCREATEPATTERNBRUSH

	case 0x0149: strcpy(ObjType,"!SetLayout"); break;	// META_SETLAYOUT

	case 0x014C: strcpy(ObjType,"!ResetDc"); break;		//?
	case 0x014D: strcpy(ObjType,"!StartDoc"); break;	//?

	case 0x01F0: parse_DeleteObject(); break;		// META_DELETEOBJECT

	case 0x01F9: strcpy(ObjType,"!CreatePatternBrush"); break; // META_CREATEPATTERNBRUSH

	case 0x0201: parse_SetBkColor(); break;			// META_SETBKCOLOR

	case 0x0209: parse_SetTextColor(); break;		// META_SETTEXTCOLOR
	case 0x020A: strcpy(ObjType,"!SetTextJustification"); break;// META_SETTEXTJUSTIFICATION
	case 0x020B: parse_SetWindowOrg(); break;		// META_SETWINDOWORG
	case 0x020C: parse_SetWindowExt(); break;		// META_SETWINDOWEXT
	case 0x020D: strcpy(ObjType,"!SetViewportOrg"); break;	// META_SETVIEWPORTORG
	case 0x020E: strcpy(ObjType,"!SetViewportExt"); break;	// META_SETVIEWPORTEXT
	case 0x020F: strcpy(ObjType,"!OffsetWindowOrg"); break;	// META_OFFSETWINDOWORG

	case 0x0211: strcpy(ObjType,"!OffsetViewportOrg"); break; // META_OFFSETVIEWPORTORG
	case 0x0213: parse_LineTo(VectList); break;		// META_LINETO
	case 0x0214: parse_MoveTo(); break;			// META_MOVETO

	case 0x0220: strcpy(ObjType,"!OffsetClipRgn"); PleaseReport(ObjType+1); break; // META_OFFSETCLIPRGN

	case 0x0228: strcpy(ObjType,"!FillRegion"); PleaseReport(ObjType+1); break; // META_FILLREGION

	case 0x0231: strcpy(ObjType,"!SetMapperFlags"); break;  // META_SETMAPPERFLAGS  No idea how to convert.

	case 0x0234: parse_SelectPalette(); break;		// META_SELECTPALETTE

	case 0x02FA: parse_CreatePenIndirect(); break;		// META_CREATEPENINDIRECT
	case 0x02FB: parse_FontIndirect(); break;		// META_CREATEFONTINDIRECT
	case 0x02FC: parse_CreateBrushIndirect(); break;	// META_CREATEBRUSHINDIRECT
	case 0x02FD: strcpy(ObjType,"!CreateBitmapIndirect"); break;

	case 0x0324: parse_Polygon(VectList); break;		// META_POLYLINE
	case 0x0325: parse_PolyLine(VectList); break;		// META_POLYLINE

	case 0x0410: strcpy(ObjType,"!ScaleWindowExt"); break;	// META_SCALEWINDOWEXT

	case 0x0412: strcpy(ObjType,"!ScaleViewportExt"); break;// META_SCALEVIEWPORTEXT

	case 0x0415: strcpy(ObjType,"!ExcludeClipRect"); break;	// META_EXCLUDECLIPRECT
	case 0x0416: strcpy(ObjType,"!IntersectClipRect"); break; // META_INTERSECTCLIPRECT

	case 0x0418: parse_Ellipse(VectList); break;	// META_ELLIPSE
	case 0x0419: strcpy(ObjType,"!FloodFill"); PleaseReport(ObjType+1); break; // META_FLOODFILL

	case 0x041B: parse_Rectangle(VectList); break;	// META_RECTANGLE

	case 0x041F: parse_SetPixel(VectList); break;	// META_SETPIXEL

	case 0x0429: strcpy(ObjType,"!FrameRegion"); break; // META_FRAMEREGION

	case 0x0436: strcpy(ObjType,"!AnimatePalette"); break; // META_ANIMATEPALETTE

	case 0x0521: parse_TextOut(VectList); break;	// META_TEXTOUT
	case 0x0538: parse_PolyPolygon(VectList); break;// META_POLYPOLYGON

	case 0x0548: strcpy(ObjType,"!ExtFloodFill"); PleaseReport(ObjType+1); break; // META_EXTFLOODFILL

	case 0x061C: parse_RoundRect(VectList); break;	// META_ROUNDRECT
	case 0x061D: strcpy(ObjType,"!PatBlt"); PleaseReport(ObjType+1); break; // META_PATBLT

	case 0x626: wmf_ESCAPE(); break;		// META_ESCAPE

	case 0x062F: strcpy(ObjType,"!DrawText"); PleaseReport(ObjType+1); break;

	case 0x06FE: strcpy(ObjType,"!CreateBitmap"); PleaseReport(ObjType+1); break;
	case 0x06FF: strcpy(ObjType,"!CreateRegion"); break;	// META_CREATEREGION

	case 0x0817: parse_Arc(VectList); break;	// META_ARC

	case 0x081A: parse_Pie(VectList); break;	// META_PIE

	case 0x0830: parse_Chord(); break;		// META_CHORD

	case 0x0922: strcpy(ObjType,"!BitBlt"); PleaseReport(ObjType+1); break; // META_BITBLT

	case 0x0940: parse_DibBitBlt(); break;		// META_DIBBITBLT

	case 0x0A32: parse_ExtTextOut(VectList); break;	// META_EXTTEXTOUT

	case 0x0B23: strcpy(ObjType,"!StretchBlt"); PleaseReport(ObjType+1); break; // META_STRETCHBLT

	case 0x0B41: parse_DibStretchBlt(); break;	// META_DIBSTRETCHBLT

	case 0x0d33: strcpy(ObjType,"!SetDibToDev"); PleaseReport(ObjType+1); break; // META_SETDIBTODEV

	case 0x0F43: StretchDIBits(); break;		// META_STRETCHDIB

	case 0x1000: strcpy(ObjType,"!BeginPath"); PleaseReport(ObjType+1); break; // BEGIN_PATH
	case 0x1001: strcpy(ObjType,"!ClipToPath"); PleaseReport(ObjType+1); break;// CLIP_TO_PATH
	case 0x1002: strcpy(ObjType,"!EndPath"); PleaseReport(ObjType+1); break;   // END_PATH

	case 0x1017: strcpy(ObjType,"!CheckJpgFormat"); PleaseReport(ObjType+1); break;   //CHECKJPEGFORMAT
	case 0x1018: strcpy(ObjType,"!CheckPngFormat"); PleaseReport(ObjType+1); break;   //CHECKPNGFORMAT

	default: snprintf(ObjType, sizeof(ObjType),"!Unknown#%X", WmfRec.Function);
	}

      if(ObjType[0]=='!') UnknownObjects++;

      if(log!=NULL)
	{
	fprintf(log,_("\n%*s{WMF function:%3Xh; size:%4lu; pos:%lXh}"),
		  recursion*2,"",WmfRec.Function, (long)2*WmfRec.Size, (long)ActualPos);
	if(*ObjType!=0) fprintf(log," %s",ObjType);

        if(ObjType[0] == '!')
          {
	  fprintf(log,"{");
	  fseek(wpd,WmfRec.ParamFilePos,SEEK_SET);
	  for(int i=0; i<(int)WmfRec.Size-3; i++)
	    //fprintf(log,"%X ",WmfRec->Parameters[i]);
	    {
	    uint16_t w;
            Rd_word(wpd,&w);
	    if(feof(wpd)) break;
	    int j = w % 256;
	    fprintf(log, (i==0)?"%X":",%X", j);
	    if(j>=' ') fprintf(log,"(%c)",j);
	    j = w / 256;
	    fprintf(log,",%X",j);
	    if(j>=' ') fprintf(log,"(%c)",j);

	    if(i>100) {fprintf(log," ... ");break;}
	    }
	  fprintf(log,"}");
          }
	}

      if(*ObjType==0) UnknownObjects++;

      if(NewPos!=0) ActualPos = NewPos;
	       else ActualPos+= 2*WmfRec.Size;

      fseek(wpd,ActualPos,SEEK_SET);
      if(WmfRec.Function==0)
          break;		// Break loop on WMF End
      }
    else
      ActualPos = ftell(wpd);
    }

#ifdef _DEBUG
    //{FILE *F=fopen("O:\\temp\\39\\wmf_file.txt","wb");VectList.Dump2File(F);fclose(F);}
    //Img.AttachVecImg(new VectorImage(VectList,PSS));
    //SavePictureEPS("o:\\temp\\40\\wmf_file.eps",Img);
#endif

    const float Scale = GetScale2PSU((TMapMode)MapMode) * 25.4f / 71.0f;	// convert PSu to WPGu (quite bad).
    vFlip flipTrx(bbx.MinY, bbx.MaxY);

    Image *Img2 = &Img;
    while(Img2!=NULL)
    {
      if(Img2->Raster!=NULL)
      {
        if(Img2->dy<0)
        {
          Img2->dy = -Img2->dy;
          //Img2->y += Img2->dy;
          Flip2D(Img2->Raster);
        }
        else
        {
          Img2->y += Img2->dy;
        }

        if(Img2->dx<0)
        {        
         Img2->x += Img2->dx;
         Img2->dx = -Img2->dx;
         Flip1D(Img2->Raster);
        }

        if(YExtent>=0)
            flipTrx.ApplyTransform(Img2->x, Img2->y);     
      }
      //if(Img2->Vector)
      //   Img2->Vector.Transform(flipTx);
      Img2->dx *= Scale;
      Img2->dy *= Scale;
      Img2->x *= Scale;
      Img2->y = Img2->y*Scale - Img2->dy;
      Img2 = Img2->Next;
    }

  if(YExtent>=0)
      VectList.Transform(flipTrx);

  //if(PSData.length() > 0)
  //    VectList.AddObject(new PsBlob(PSData.ExtractString()));  

  if(VectList.VectorObjects>0 && Img.VecImage==NULL)
  {    
    Img.AttachVecImg(new VectorImage(VectList,PSS));

    if(Img.dx!=0 && Img.dy!=0 && Img.Raster!=NULL)
    {
      if(Img.VecImage!=NULL)	// Move raster data to different image frame.
      {
        Img2 = &Img;
        while(Img2->Next!=NULL)
          Img2 = Img2->Next;
        Img2->Next = new Image();
        Img2 = Img2->Next;

        Img2->x =  bbx.MinX * Scale;
        Img2->y =  bbx.MinY * Scale;
        Img2->dx = (bbx.MaxX - bbx.MinX) * Scale;
        Img2->dy = (bbx.MaxY - bbx.MinY) * Scale;
        Img2->VecImage = Img.VecImage; Img.VecImage=NULL;
      }
    }
    else	// Use whole frame as bounding box.
    {
      Img.x =  bbx.MinX * Scale;
      Img.y =  bbx.MinY * Scale;
      Img.dx = (bbx.MaxX - bbx.MinX) * Scale;
      Img.dy = (bbx.MaxY - bbx.MinY) * Scale;  
    }
  }
return 0;
}


/*******************************************************************/
/* This procedure provides all needed processing for the first pass*/
int TconvertedPass1_WMF::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_WMF() ");fflush(log);
#endif
int RetVal = 0;
const uint16_t CodePageBk = OutCodePage;

  if(Verbosing >= 1) printf(_("Opening Windows MetaFile:"));

  LoadImageWMF();

  if(!NoConvertImage && !Img.isEmpty())
  {
    for(Image *pImg=&Img; pImg!=NULL; pImg=pImg->Next)
      if(pImg->Raster!=NULL)
        ReducePalette(pImg,256);

    string NewFilename = MergePaths(OutputDir,RelativeFigDir);
    string wpd_cut = CutFileName(wpd_filename);
    if(recursion==0 && length(wpd_cut)>0)
      NewFilename += wpd_cut + ".eps";
    else
      NewFilename += GetFullFileName(GetSomeImgName(".eps"));

    if(SavePictureEPS(NewFilename(),Img)<0)
	{
        if(err != NULL)
	  {
	  perc.Hide();
	  fprintf(err, _("\nError: Cannot save file: \"%s\"!"), NewFilename());
	  }
	return 0;
        }

    NewFilename = CutFileName(NewFilename); 	//New Filename only

    PutImageIncluder(NewFilename());

    InputPS |= 1;		//mark style as used
    Finalise_Conversion(this);
    RetVal++;
  }

  OutCodePage = CodePageBk;
  return(RetVal);
}


/* WMF files usually have special header before data. */

typedef struct _WmfSpecialHeader
{
  uint32_t Key;			///< Magic number (always 9AC6CDD7h)
  uint16_t  Handle;		///< Metafile HANDLE number (always 0)
  int16_t Left;			///< Left coordinate in metafile units
  int16_t Top;			///< Top coordinate in metafile units
  int16_t Right;		///< Right coordinate in metafile units
  int16_t Bottom;		///< Bottom coordinate in metafile units
  uint16_t  Inch;		//< Number of metafile units per inch.
  uint32_t Reserved;		///< Reserved (always 0)
  uint16_t  Checksum;		///< Checksum value for previous 10 WORDs
} WMFSPECIAL;


static inline long LoadWMFSpecial(FILE *f, _WmfSpecialHeader & WSH)
{
#if defined(__PackedStructures__)
return(fread(&WSH,1,sizeof(_WmfSpecialHeader),f));
#else
return(loadstruct(f,"dwwwwwwdw",
      &WSH.Key, &WSH.Handle,
      &WSH.Left, &WSH.Top, &WSH.Right, &WSH.Bottom,
      &WSH.Inch, &WSH.Reserved, &WSH.Checksum));
#endif
}


class TconvertedPass1_WMFspec: public TconvertedPass1_WMF
     {
public:
     virtual int Convert_first_pass(void);
     };


int TconvertedPass1_WMFspec::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_WMF() ");fflush(log);
#endif

  {
  WMFSPECIAL hdr;
  if(LoadWMFSpecial(wpd,hdr)!=22) return -1;
  if(hdr.Key!=0x9AC6CDD7) return -2;

  uint32_t Checksum = hdr.Key;
  Checksum ^= hdr.Handle;
  Checksum ^= (uint16_t)hdr.Left;	// Convert to unsigned to prevent sign expansion.
  Checksum ^= (uint16_t)hdr.Top;
  Checksum ^= (uint16_t)hdr.Right;
  Checksum ^= (uint16_t)hdr.Bottom;
  Checksum ^= hdr.Inch;
  Checksum ^= hdr.Reserved;

  Checksum = (Checksum & 0x0000FFFFUL) ^ ((Checksum & 0xFFFF0000UL) >> 16);
  if(Checksum != hdr.Checksum)
    {
    if(err != NULL)
      {      
      fprintf(err, _("\nWarning: Invalid WMF CRC %X, expected %X."),hdr.Checksum,Checksum);
      }
    }
  }  // scope discards hdr

 return TconvertedPass1_WMF::Convert_first_pass();
}

/* Register translator for WMF with special header here. */
TconvertedPass1 *Factory_WMFspec(void) {return new TconvertedPass1_WMFspec;}
FFormatTranslator FormatWMFWrapperSpecial("WMFspecial",Factory_WMFspec);



Image LoadPictureWMF(const char *Name)
{
TconvertedPass1_WMF ConvWMF;
size_t fsize;

  ConvWMF.err = stderr;
  ConvWMF.strip = ConvWMF.log = NULL;
  ConvWMF.flag = NormalText;
  if(Name)
  {
    ConvWMF.wpd = fopen(Name,"rb");
    if(ConvWMF.wpd)
    {
      ConvWMF.PositionX = ConvWMF.PositionY = 0;
      //ConvWMF.ConvertHTML = GetTranslator("htmlTOinternal");
      //ConvWMF.CharReader = &ch_fgetc;

      //ConvWMF.TablePos=0;
      ConvWMF.ActualPos = ConvWMF.DocumentStart = ftell(ConvWMF.wpd);
      fsize = FileSize(ConvWMF.wpd);

      ConvWMF.LoadImageWMF();
      //ConvSvg.perc.Init(ftell(ConvSvg.wpd), fsize,_("First pass SVG:") );

      fclose(ConvWMF.wpd);
      ConvWMF.wpd = NULL;

      //ConvWMF.ProcessImage();
    }
  }
  return ConvWMF.Img;
}


#ifndef ImplementReader
#define ImplementReader(SUFFIX,EXTENSION) \
Image LoadPicture##SUFFIX(const char *Name);\
int SavePicture##SUFFIX(const char *Name,const Image &Img);\
TImageFileHandler HANDLER_##SUFFIX(EXTENSION,
#endif

ImplementReader(WMF,".WMF")
   LoadPictureWMF,
   NULL);



//////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// EMF //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

typedef struct _EnhancedMetaRecord
{
  uint32_t Type;		///< An unsigned integer that identifies this record type.
  uint32_t Size;		///< An unsigned integer that specifies the size of this record in bytes.
  uint32_t ParamFilePos;	///< Index to the file, not contained in data.    
} EMFRECORD;


/** Functor class for EMF world transform. */
class TransformEMF: public AbstractTransformXY
{
public:
  float M11, M12, M21, M22;
  float Dx, Dy;
  
  TransformEMF(void) {M11=M22=1; M21=M12=Dx=Dy=0;}

  virtual void ApplyTransform(float &x, float &y) const;

  float TopPBottom;
};


void TransformEMF::ApplyTransform(float &x, float &y) const
{
  const float xNew = M11*x + M21*y + Dx;
  y = M12*x + M22*y + Dy;
  x = xNew;
}


class TconvertedPass1_EMF: public TconvertedPass1_xMF
{
public:
    int32_t PositionX, PositionY;
    CpTranslator *ConvertUnicode;
    TransformEMF WorldTx;

    TconvertedPass1_EMF(void) {PositionX = PositionY = 0;}
    int LoadImageEMF(void);

    virtual int Convert_first_pass(void);

    void parse_CreateBrushIndirect(void);
    void parse_CreatePalette(void);
    void parse_CreatePen(void);
    void parse_DeleteObject(void);
    void parse_Ellipse(VectorList &VectList);
    void parse_ExtCreateFontIndirectW(void);
    void parse_ExtCreatePen(void);
    void parse_ExtTextOutW(VectorList &VectList);
    void parse_LineTo(VectorList &VectList);
    void parse_ModifyWorldTransform(void);
    void parse_SetWorldTransform(void);
    void parse_MoveToEX(void);
    void parse_PolyBezier16(VectorList &VectList);
    void parse_PolyBezierTo16(VectorList &VectList);
    void parse_Polygon16(VectorList &VectList);
    void parse_PolyLine16(VectorList &VectList);
    void parse_PolyLineTo16(VectorList &VectList);
    void parse_PolyPolygon16(VectorList &VectList);
    void parse_PolyPolyLine16(VectorList &VectList);
    void parse_Rectangle(VectorList &VectList);
    void parse_RestoreDC(void);
    void parse_SaveDC(void);
    void parse_SelectObject(void);
    void parse_SelectPalette(void);
    void parse_SetBkColor(void);
    void parse_SetBkMode(void);
    void parse_SetPixel(VectorList &VectList);
    void parse_SetPolyFillMode(void);
    void parse_SetTextAlign(void);
    void parse_SetTextColor(void);
    void parse_StretchDIBits(VectorList &VectList);

    _EnhancedMetaRecord EmfRec;
};

TconvertedPass1 *Factory_EMF(void) {return new TconvertedPass1_EMF;}
FFormatTranslator FormatEMFWrapper("EMF",Factory_EMF);



typedef struct
{
  uint32_t Bounds[4];	///< Rectangular inclusive-inclusive bounds in logical units of the smallest rectangle
  uint32_t Frame[4];	///< Rectangular inclusive-inclusive dimensions, in .01 millimeter units, of a rectangle that surrounds the image stored in the metafile.
  uint32_t Signature;
  uint32_t Version;
  uint32_t Bytes;
  uint32_t Records;
  uint16_t  Handles;	///< number of graphics objects that are used during the processing of the metafile.
  uint16_t  Reserved;	///< An unsigned integer that MUST be 0x0000 and MUST be ignored.
  uint32_t nDescription;	///< Number of characters in the array that contains the description of the metafile's contents.
  uint32_t offDescription;	///< offset from the beginning of this record to the array that contains the description of the metafile's contents.
  uint32_t nPalEntries;	///< Number of entries in the metafile palette.
  uint32_t Device[2];	///< A SizeL object ([MS-WMF] section 2.2.2.22) that specifies the size of the reference device, in pixels. 
  uint32_t Millimeters[2];	///< A SizeL object that specifies the size of the reference device, in millimeters.
} EMFHEAD;


typedef  enum  
 { 
   EMR_HEADER = 0x00000001, 
   EMR_POLYBEZIER = 0x00000002, 
   EMR_POLYGON = 0x00000003, 
   EMR_POLYLINE = 0x00000004, 
   EMR_POLYBEZIERTO = 0x00000005, 
   EMR_POLYLINETO = 0x00000006,
   EMR_POLYPOLYLINE = 0x00000007, 
   EMR_POLYPOLYGON = 0x00000008, 
   EMR_SETWINDOWEXTEX = 0x00000009, 
   EMR_SETWINDOWORGEX = 0x0000000A, 
   EMR_SETVIEWPORTEXTEX = 0x0000000B, 
   EMR_SETVIEWPORTORGEX = 0x0000000C, 
   EMR_SETBRUSHORGEX = 0x0000000D, 
   EMR_EOF = 0x0000000E, 
   EMR_SETPIXELV = 0x0000000F, 
   EMR_SETMAPPERFLAGS = 0x00000010, 
   EMR_SETMAPMODE = 0x00000011, 
   EMR_SETBKMODE = 0x00000012, 
   EMR_SETPOLYFILLMODE = 0x00000013, 
   EMR_SETROP2 = 0x00000014, 
   EMR_SETSTRETCHBLTMODE = 0x00000015, 
   EMR_SETTEXTALIGN = 0x00000016, 
   EMR_SETCOLORADJUSTMENT = 0x00000017, 
   EMR_SETTEXTCOLOR = 0x00000018, 
   EMR_SETBKCOLOR = 0x00000019, 
   EMR_OFFSETCLIPRGN = 0x0000001A, 
   EMR_MOVETOEX = 0x0000001B, 
   EMR_SETMETARGN = 0x0000001C, 
   EMR_EXCLUDECLIPRECT = 0x0000001D, 
   EMR_INTERSECTCLIPRECT = 0x0000001E, 
   EMR_SCALEVIEWPORTEXTEX = 0x0000001F, 
   EMR_SCALEWINDOWEXTEX = 0x00000020, 
   EMR_SAVEDC = 0x00000021, 
   EMR_RESTOREDC = 0x00000022, 
   EMR_SETWORLDTRANSFORM = 0x00000023, 
   EMR_MODIFYWORLDTRANSFORM = 0x00000024, 
   EMR_SELECTOBJECT = 0x00000025, 
   EMR_CREATEPEN = 0x00000026, 
   EMR_CREATEBRUSHINDIRECT = 0x00000027, 
   EMR_DELETEOBJECT = 0x00000028, 
   EMR_ANGLEARC = 0x00000029, 
   EMR_ELLIPSE = 0x0000002A, 
   EMR_RECTANGLE = 0x0000002B, 
   EMR_ROUNDRECT = 0x0000002C, 
   EMR_ARC = 0x0000002D, 
   EMR_CHORD = 0x0000002E,
   EMR_PIE = 0x0000002F, 
   EMR_SELECTPALETTE = 0x00000030, 
   EMR_CREATEPALETTE = 0x00000031, 
   EMR_SETPALETTEENTRIES = 0x00000032, 
   EMR_RESIZEPALETTE = 0x00000033, 
   EMR_REALIZEPALETTE = 0x00000034, 
   EMR_EXTFLOODFILL = 0x00000035, 
   EMR_LINETO = 0x00000036, 
   EMR_ARCTO = 0x00000037, 
   EMR_POLYDRAW = 0x00000038, 
   EMR_SETARCDIRECTION = 0x00000039, 
   EMR_SETMITERLIMIT = 0x0000003A, 
   EMR_BEGINPATH = 0x0000003B, 
   EMR_ENDPATH = 0x0000003C, 
   EMR_CLOSEFIGURE = 0x0000003D, 
   EMR_FILLPATH = 0x0000003E, 
   EMR_STROKEANDFILLPATH = 0x0000003F, 
   EMR_STROKEPATH = 0x00000040, 
   EMR_FLATTENPATH = 0x00000041, 
   EMR_WIDENPATH = 0x00000042, 
   EMR_SELECTCLIPPATH = 0x00000043, 
   EMR_ABORTPATH = 0x00000044, 
   EMR_COMMENT = 0x00000046, 
   EMR_FILLRGN = 0x00000047, 
   EMR_FRAMERGN = 0x00000048, 
   EMR_INVERTRGN = 0x00000049, 
   EMR_PAINTRGN = 0x0000004A, 
   EMR_EXTSELECTCLIPRGN = 0x0000004B, 
   EMR_BITBLT = 0x0000004C, 
   EMR_STRETCHBLT = 0x0000004D, 
   EMR_MASKBLT = 0x0000004E, 
   EMR_PLGBLT = 0x0000004F, 
   EMR_SETDIBITSTODEVICE = 0x00000050, 
   EMR_STRETCHDIBITS = 0x00000051, 
   EMR_EXTCREATEFONTINDIRECTW = 0x00000052, 
   EMR_EXTTEXTOUTA = 0x00000053, 
   EMR_EXTTEXTOUTW = 0x00000054, 
   EMR_POLYBEZIER16 = 0x00000055, 
   EMR_POLYGON16 = 0x00000056, 
   EMR_POLYLINE16 = 0x00000057, 
   EMR_POLYBEZIERTO16 = 0x00000058,
   EMR_POLYLINETO16 = 0x00000059, 
   EMR_POLYPOLYLINE16 = 0x0000005A, 
   EMR_POLYPOLYGON16 = 0x0000005B, 
   EMR_POLYDRAW16 = 0x0000005C, 
   EMR_CREATEMONOBRUSH = 0x0000005D, 
   EMR_CREATEDIBPATTERNBRUSHPT = 0x0000005E, 
   EMR_EXTCREATEPEN = 0x0000005F, 
   EMR_POLYTEXTOUTA = 0x00000060, 
   EMR_POLYTEXTOUTW = 0x00000061, 
   EMR_SETICMMODE = 0x00000062, 
   EMR_CREATECOLORSPACE = 0x00000063, 
   EMR_SETCOLORSPACE = 0x00000064, 
   EMR_DELETECOLORSPACE = 0x00000065, 
   EMR_GLSRECORD = 0x00000066, 
   EMR_GLSBOUNDEDRECORD = 0x00000067, 
   EMR_PIXELFORMAT = 0x00000068, 
   EMR_DRAWESCAPE = 0x00000069, 
   EMR_EXTESCAPE = 0x0000006A, 
   EMR_SMALLTEXTOUT = 0x0000006C, 
   EMR_FORCEUFIMAPPING = 0x0000006D, 
   EMR_NAMEDESCAPE = 0x0000006E, 
   EMR_COLORCORRECTPALETTE = 0x0000006F, 
   EMR_SETICMPROFILEA = 0x00000070, 
   EMR_SETICMPROFILEW = 0x00000071, 
   EMR_ALPHABLEND = 0x00000072, 
   EMR_SETLAYOUT = 0x00000073, 
   EMR_TRANSPARENTBLT = 0x00000074, 
   EMR_GRADIENTFILL = 0x00000076,
   EMR_SETLINKEDUFIS = 0x00000077, 
   EMR_SETTEXTJUSTIFICATION = 0x00000078, 
   EMR_COLORMATCHTOTARGETW = 0x00000079, 
   EMR_CREATECOLORSPACEW = 0x0000007A 
 } EmfRecordType;



inline long LoadEMFHeader(FILE *f, EMFHEAD & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(EMFHEAD),f));
#else
return(loadstruct(f,"ddddddddddddwwddddddd",
	&SU.Bounds[0], &SU.Bounds[1], &SU.Bounds[2], &SU.Bounds[3],
        &SU.Frame[0], &SU.Frame[1], &SU.Frame[2], &SU.Frame[3],
	&SU.Signature, &SU.Version, &SU.Bytes, &SU.Records, &SU.Handles, &SU.Reserved,
        &SU.nDescription, &SU.offDescription, &SU.nPalEntries, 
        &SU.Device[0], &SU.Device[1], &SU.Millimeters[0], &SU.Millimeters[1]));
#endif
}


/** Load header for one EMF record. */
bool LoadEmfRecord(FILE *f, _EnhancedMetaRecord &EmfRec)
{
  EmfRec.ParamFilePos = ftell(f);
  Rd_dword(f,&EmfRec.Type);
  Rd_dword(f,&EmfRec.Size);
  if(feof(f) || EmfRec.Size<3) return false;    
  return true;  
}


void TconvertedPass1_EMF::parse_CreateBrushIndirect(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_CreateBrushIndirect ");fflush(log);
#endif
static const char ObjName[] = "!CREATEBRUSHINDIRECT";

  if(EmfRec.Size >= 8+4+12)
    {
    uint32_t ihBrush;
    uint32_t BrushStyle, BrushColor, BrushHatch;

    Rd_dword(wpd, &ihBrush);

    Rd_dword(wpd, &BrushStyle);
    switch(BrushStyle)
    {    
      case BS_SOLID:	PSS.FillPattern=FILL_SOLID; break;
      case BS_HATCHED:	PSS.FillPattern=FILL_DIAG_UP; break;
      case BS_NULL:
      default:		PSS.FillPattern=FILL_NONE; break;
    }

    Rd_dword(wpd, &BrushColor);
    PSS.FillColor.Red   = BrushColor & 0xFF;
    PSS.FillColor.Green = (BrushColor>>8) & 0xFF;
    PSS.FillColor.Blue  = (BrushColor>>16) & 0xFF;    

    Rd_dword(wpd, &BrushHatch);

    ObjTab.AddObject(new vecBrush(PSS));
    strcpy(ObjType,ObjName+1);
    return;
    }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_ExtCreateFontIndirectW(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_ExtCreateFontIndirectW() ");fflush(log);
#endif
static const char ObjName[] = "!EXTCREATEFONTINDIRECTW";
uint32_t ihFonts;
int32_t Height, Width;
uint32_t Escapement;	//, Orientation;
uint32_t D;
uint8_t Underline, Strikeout, Charset, OutPrecision, ClipPrecision, Quality, PitchAndFamily;

  /*if(!CheckWmfRecSz(9))
  {
    ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }*/

  Rd_dword(wpd, &ihFonts);
  Rd_dword(wpd, (uint32_t*)&Height);
  Rd_dword(wpd, (uint32_t*)&Width);
  Rd_dword(wpd, &Escapement);
  Rd_dword(wpd,&D); PSS.FontOrientation10 = D;
  Rd_dword(wpd,&D); PSS.FontWeight = D;

  PSS.FontItallic = fgetc(wpd);
  Underline = fgetc(wpd);
  Strikeout = fgetc(wpd);
  Charset = fgetc(wpd);

  switch(Charset)
  {
    case OEM_CHARSET:
    case DEFAULT_CHARSET:	ConvertCpg = NULL;
				break;
    case ANSI_CHARSET:		ConvertCpg = GetTranslator("cp1252TOinternal");	break;
    case SYMBOL_CHARSET:	ConvertCpg = GetTranslator("symbolTOinternal");	break;
    case EASTEUROPE_CHARSET:	ConvertCpg = GetTranslator("cp1250TOinternal"); // see https://stackoverflow.com/questions/22911186/windows-encoding-clarification
				break;
    case RUSSIAN_CHARSET:	ConvertCpg = GetTranslator("cp1251TOinternal");	break;
    case MAC_CHARSET:		ConvertCpg = GetTranslator("MacRomanTOinternal"); break;
    case GREEK_CHARSET:		ConvertCpg = GetTranslator("cp1253TOinternal"); // see https://www.compart.com/en/unicode/charsets/windows-1253
				break;
    case HEBREW_CHARSET:	ConvertCpg = GetTranslator("cp1255TOinternal"); // see https://www.compart.com/en/unicode/charsets/windows-1255
				break;
    case TURKISH_CHARSET:	ConvertCpg = GetTranslator("cp1254TOinternal"); break;
    case ARABIC_CHARSET:        ConvertCpg = GetTranslator("cp1256TOinternal"); break;
    case SHIFTJIS_CHARSET:
    case HANGUL_CHARSET:
    case JOHAB_CHARSET:
    case GB2312_CHARSET:
    case CHINESEBIG5_CHARSET:
    case VIETNAMESE_CHARSET:
    case BALTIC_CHARSET:
    case THAI_CHARSET:

    default:			ConvertCpg = NULL; break;
  }
  
  if(ConvertCpg == NULL)
     {
     perc.Hide();
     fprintf(err, _("\nUnsupported charset: %X."), (unsigned)Charset);
     }

  PSS.ConvertCpg = ConvertCpg;

  OutPrecision = fgetc(wpd);
  ClipPrecision = fgetc(wpd);
  Quality = fgetc(wpd);
  PitchAndFamily = fgetc(wpd);

/* 
  char *FaceName;
  FaceName = (char*)malloc(64);
  if(FaceName != NULL)
  {    
    fread(FaceName,64,1,wpd);
    for(int i=0; i<32; i++)
    {
      uint16_t wch = *(uint16_t*)(FaceName+2*i);
      FaceName[i] = (wch>=255) ? 255 : wch;
    }
    FaceName[32] = 0;

    if(!strcmp(FaceName,"Symbol"))
    {
    }

    free(FaceName);
    FaceName = NULL;
  }
*/

  snprintf(ObjType, sizeof(ObjType),"%s(H=%d,set=%u,rot=%d)", ObjName+1, (int)Height, (unsigned)Charset, PSS.FontOrientation10/10);

  if(YExtent<0) PSS.FontOrientation10 = -PSS.FontOrientation10;
  if(Height!=0)
  {
    if(Height>0)		// The font height is normally negative.
	PSS.FontOrientation10 = -PSS.FontOrientation10;
    PSS.FontSize = labs(Height)/2.66;
  }
  if(Width!=0) 
    PSS.FontSizeW = Width/2.66;
  else
    PSS.FontSizeW = PSS.FontSize;

  ObjTab.AddObject(new vecFont(PSS));
}


void TconvertedPass1_EMF::parse_CreatePen(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_CreatePen ");fflush(log);
#endif
static const char ObjName[] = "!CREATEPEN";

  if(EmfRec.Size >= 8+4+16)
    {
    uint32_t ihPen;
    uint32_t PenStyle, PenWidthX, PenWidthY, ColorRef;

    Rd_dword(wpd, &ihPen);

    Rd_dword(wpd, &PenStyle);

    switch(PenStyle & 0xFF)
      {    
      case PS_DASH:	PSS.LineStyle=7; break;// 0x0001, 
      case PS_DOT:	PSS.LineStyle=10; break;// 0x0002,
      case PS_DASHDOT:	PSS.LineStyle=15; break;// 0x0003,
      case PS_DASHDOTDOT: PSS.LineStyle=19; break;// 0x0004, 
      case PS_NULL:	PSS.LineStyle=0; break;	// 0x0005, 
      //case PS_INSIDEFRAME = 0x0006, 
      //case PS_USERSTYLE = 0x0007, 
      //case PS_ALTERNATE = 0x0008,
      case PS_SOLID:	
      default:
			PSS.LineStyle=1; break;	// 0x0000, 
      }
    PenWidthX = PenStyle & 0x0F00;
    switch(PenWidthX)
    {
      case PS_ENDCAP_ROUND:	PSS.LineCap=1; break;	// 0x0000 1 (round caps)
      case PS_ENDCAP_SQUARE:	PSS.LineCap=2; break;	// 0x0100 2 (extended butt caps). 
      case PS_ENDCAP_FLAT:	PSS.LineCap=0; break;	// 0x0200 0 (butt caps)
    }
    PenWidthX = PenStyle & 0xF000;
    switch(PenWidthX)
    {
      case PS_JOIN_ROUND:	PSS.LineJoin=1; break;	// 0x0000 1 (round join)
      case PS_JOIN_BEVEL:	PSS.LineJoin=2; break;	// 0x1000 2 (bevel join)
      case PS_JOIN_MITER:	PSS.LineJoin=0; break;	// 0x2000 0 (miter join)
    }

    Rd_dword(wpd, &PenWidthX);
    Rd_dword(wpd, &PenWidthY);		// The value of its y field MUST be ignored.
    PSS.LineWidth = GetScale2PSU((TMapMode)MapMode) * PenWidthX;

    Rd_dword(wpd, &ColorRef);
    PSS.LineColor.Red   = ColorRef & 0xFF;
    PSS.LineColor.Green = (ColorRef>>8) & 0xFF;
    PSS.LineColor.Blue  = (ColorRef>>16) & 0xFF;

    ObjTab.AddObject(new vecPen(PSS));

    strcpy(ObjType,ObjName+1);
    return;
    }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_ExtCreatePen(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_ExtCreatePen ");fflush(log);
#endif
static const char ObjName[] = "!EXTCREATEPEN";

  if(EmfRec.Size >= 8+4+16+20)
    {
    uint32_t ihPen;
    uint32_t offBmi, cbBmi, offBits, cbBits;

    Rd_dword(wpd, &ihPen);
    Rd_dword(wpd, &offBmi);
    Rd_dword(wpd, &cbBmi);
    Rd_dword(wpd, &offBits);
    Rd_dword(wpd, &cbBits);

    uint32_t PenStyle, PenWidth, BrushStyle, ColorRef, BrushHatch;

    Rd_dword(wpd, &PenStyle);
    switch(PenStyle & 0xFF)
      {    
      case PS_DASH:	PSS.LineStyle=7; break;// 0x0001, 
      case PS_DOT:	PSS.LineStyle=10; break;// 0x0002,
      case PS_DASHDOT:	PSS.LineStyle=15; break;// 0x0003,
      case PS_DASHDOTDOT: PSS.LineStyle=19; break;// 0x0004, 
      case PS_NULL:	PSS.LineStyle=0; break;	// 0x0005, 
      //case PS_INSIDEFRAME = 0x0006, 
      //case PS_USERSTYLE = 0x0007, 
      //case PS_ALTERNATE = 0x0008,
      case PS_SOLID:	
      default:
			PSS.LineStyle=1; break;	// 0x0000, 
      }
    PenWidth = PenStyle & 0x0F00;
    switch(PenWidth)
    {
      case PS_ENDCAP_ROUND:	PSS.LineCap=1; break;	// 0x0000
      case PS_ENDCAP_SQUARE:	PSS.LineCap=2; break;	// 0x0100
      case PS_ENDCAP_FLAT:	PSS.LineCap=0; break;	// 0x0200
    }
    PenWidth = PenStyle & 0xF000;
    switch(PenWidth)
    {
      case PS_JOIN_ROUND:	PSS.LineJoin=1; break;	// 0x0000 1 (round join)
      case PS_JOIN_BEVEL:	PSS.LineJoin=2; break;	// 0x1000 2 (bevel join)
      case PS_JOIN_MITER:	PSS.LineJoin=0; break;	// 0x2000 0 (miter join)
    }

    Rd_dword(wpd, &BrushStyle);
    Rd_dword(wpd, &PenWidth);
    PSS.LineWidth = GetScale2PSU((TMapMode)MapMode) * PenWidth;

    Rd_dword(wpd, &ColorRef);
    PSS.LineColor.Red   = ColorRef & 0xFF;
    PSS.LineColor.Green = (ColorRef>>8) & 0xFF;
    PSS.LineColor.Blue  = (ColorRef>>16) & 0xFF;

    Rd_dword(wpd, &BrushHatch);

    ObjTab.AddObject(new vecPen(PSS));

    snprintf(ObjType, sizeof(ObjType),"%s(W=%u)", ObjName+1, PenWidth);
    return;
    }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_CreatePalette(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_CreatePalette() ");fflush(log);
#endif
static const char ObjName[] = "!CREATEPALETTE";

  if(EmfRec.Size < 8+8)
    {
RecordCorrupted:
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType, ObjName);
    return;
    }

  uint32_t ihPal;
  Rd_dword(wpd, &ihPal);

  uint16_t NumberOfEntries;
  Rd_word(wpd, &NumberOfEntries);	// Version MUST be 0x0300.
  if(NumberOfEntries!=0x300) goto RecordCorrupted;
  Rd_word(wpd, &NumberOfEntries);

  PSS.AttachPalette(NULL);

  PSS.pPalette = BuildPalette(NumberOfEntries,8);
  if(PSS.pPalette==NULL) goto RecordCorrupted;
  PSS.pPalette->UsageCount = 1;

  for(unsigned i=0; i<NumberOfEntries; i++)
  {
    uint32_t d;
    RGBQuad RGB;

    Rd_dword(wpd, &d);
    RGB.R = d & 0xFF;
    RGB.G = (d >> 8) & 0xFF;
    RGB.B = (d >> 8) & 0xFF;
    PSS.pPalette->Set(i,&RGB);
  }

  ObjTab.AddObject(new attrPalette(PSS.pPalette));
  snprintf(ObjType, sizeof(ObjType), "%s(%u)", ObjName+1, NumberOfEntries);
}


void TconvertedPass1_EMF::parse_DeleteObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_DeleteObject() ");fflush(log);
#endif
uint32_t ObjectIndex;
static const char ObjName[] = "!DELETEOBJECT";

  if(EmfRec.Size >= 8+4)
  { 
  Rd_dword(wpd, &ObjectIndex);
  if(ObjectIndex!=0)
    {
    ObjTab.DeleteObject(ObjectIndex-1);
    snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);  
    return;
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_Ellipse(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Ellipse() ");fflush(log);
#endif
static const char ObjName[] = "!ELLIPSE";

  if(EmfRec.Size >= 8+16)
    {
    int32_t BottomRect, TopRect, RightRect, LeftRect;

    Rd_dword(wpd, (uint32_t*)&LeftRect);
    Rd_dword(wpd, (uint32_t*)&TopRect);
    Rd_dword(wpd, (uint32_t*)&RightRect);
    Rd_dword(wpd, (uint32_t*)&BottomRect);

	// TODO: Bounding box musí být upraven podle WorldTx.
    UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

    const float Scale = GetScale2PSU((TMapMode)MapMode);

    VectorEllipse *pVecEllipse = new VectorEllipse(Scale*BottomRect, Scale*TopRect, Scale*RightRect, Scale*LeftRect);
    pVecEllipse->AttribFromPSS(PSS);
  
    pVecEllipse->Transform(WorldTx);
    VectList.AddObject(pVecEllipse);

    strcpy(ObjType,ObjName+1);
    return;
    }

strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_ExtTextOutW(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_ExtTextOutW() ");fflush(log);
#endif
static const char ObjName[] = "!EXTTEXTOUTW";
short int LineLen, CurLineLen;

  int32_t Bound1, Bound2, Bound3, Bound4;
  uint32_t iGraphicsMode;

  //CrackObject(this, ActualPos+EmfRec.Size);

  Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
  Rd_dword(wpd,&iGraphicsMode);

  float exScale, eyScale;
  Rd_dword(wpd,(uint32_t*)&exScale); Rd_dword(wpd,(uint32_t*)&eyScale);

	// EMR text object follows

  int32_t y_pos, x_pos;
  Rd_dword(wpd, (uint32_t*)&x_pos);
  Rd_dword(wpd, (uint32_t*)&y_pos);

  uint32_t CharCount;
  Rd_dword(wpd, &CharCount);
  if(CharCount==0) goto ExitErr;
  if(ConvertUnicode==NULL)		// Bail out when Unicode support is not compilled :(.
  {
ExitErr:
    strcpy(ObjType,ObjName);
    return;
  }

  uint32_t OffString;	// An unsigned integer that specifies the offset to the output string in bytes, from the start of the record in which this object is contained. 
  Rd_dword(wpd, &OffString);

  fseek(wpd, OffString+ActualPos, SEEK_SET);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  TextContainer *pTextCont = new TextContainer;
  pTextCont->PosX = Scale*x_pos;
  pTextCont->PosY = Scale*y_pos + (-0.1f + 1)*mm2PSu(PSS.FontSize);	// Fix text position.
  pTextCont->TextAlign = PSS.TextAlign;
  pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
  pTextCont->MixMode = MixMode;

  if(log!=NULL && CharCount>0) fputc('\n',log);
  LineLen = CurLineLen = 0;
  while(CharCount-->0 && !feof(wpd))
  {
    uint16_t W;
    Rd_word(wpd, &W);
    if(log!=NULL)
    {
      if(W>0xFF)
        fprintf(log, "(%4.4X)", W);
      else
        fputc(W,log);
    }

    if((W&0x0FFF) == '\n')
    {
      if(CurLineLen>LineLen) LineLen=CurLineLen;
        if(!pTextCont->isEmpty())
        {
          const float CurPosY = pTextCont->PosY + mm2PSu(PSS.FontSize);
          VectList.AddObject(pTextCont);
          pTextCont = new TextContainer;
          pTextCont->PosX = Scale*x_pos;
          pTextCont->PosY = CurPosY;
          pTextCont->TextAlign = PSS.TextAlign;
          pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;
          pTextCont->MixMode = MixMode;
      }
    }
    else
    {
      CurLineLen++;
      if((W&0xF000) == 0xF000)
      {
        if(ConvertCpg != NULL)
            AddCharacterToContainer(pTextCont, (*ConvertCpg)[W&0xFF], PsNativeCP, PsNativeSym, this, PSS);
      }
      else
          AddCharacterToContainer(pTextCont, (*ConvertUnicode)[W], PsNativeCP, PsNativeSym, this, PSS);
    }
  }

  if(pTextCont->isEmpty())
    delete pTextCont;
  else
  {
    pTextCont->Transform(WorldTx);
    if(CurLineLen>LineLen) LineLen=CurLineLen;

    VectList.AddObject(pTextCont);
#if defined(_DEBUG) && defined(TEXT_DEBUG_FRAMES)
    const float sz = mm2PSu(PSS.FontSize)/2;
    float y_posf = y_pos*Scale;
    float x_posf = x_pos*Scale;
    WorldTx.ApplyTransform(x_posf,y_posf);
    VectorRectangle *pVectRec = new VectorRectangle(y_posf-sz, y_posf, pTextCont->PosX, pTextCont->PosX+sz);
    pVectRec->AttribFromPSS(PSS);
    VectList.AddObject(pVectRec);
#endif

    UpdateBBox(bbx, 0, pTextCont->PosX, pTextCont->PosY,		// Extend down reserved space for character.
             LineLen*mm2PSu(PSS.FontSizeW), -1.1*mm2PSu(PSS.FontSize));  
  }
  pTextCont = NULL;

  strcpy(ObjType,ObjName+1);
  return;
}


void TconvertedPass1_EMF::parse_ModifyWorldTransform(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_ModifyWorldTransform() ");fflush(log);
#endif
static const char ObjName[] = "!MODIFYWORLDTRANSFORM";
float M11b, M12b, M21b, M22b;
float Dxb, Dyb;


  if(EmfRec.Size < 8+6*4)
    {
    strcpy(ObjType,ObjName);
    return;
    }

  RdFLOAT_LoEnd(&M11b,wpd);
  RdFLOAT_LoEnd(&M12b,wpd);
  RdFLOAT_LoEnd(&M21b,wpd);
  RdFLOAT_LoEnd(&M22b,wpd);
  RdFLOAT_LoEnd(&Dxb,wpd);
  RdFLOAT_LoEnd(&Dyb,wpd);

  const float NewM11 = M11b*WorldTx.M11 + M21b*WorldTx.M12;
  const float NewM21 = M11b*WorldTx.M21 + M21b*WorldTx.M22;
  const float NewDx  = M11b*WorldTx.Dx + M21b*WorldTx.Dy + Dxb;

  const float NewM12 = M12b*WorldTx.M11 + M22b*WorldTx.M12;
  const float NewM22 = M12b*WorldTx.M21 + M22b*WorldTx.M22;
  const float NewDy  = M12b*WorldTx.Dx + M22b*WorldTx.Dy + Dyb;

  WorldTx.M11 = NewM11;
  WorldTx.M21 = NewM21;
  WorldTx.M12 = NewM12;
  WorldTx.M22 = NewM22;
  WorldTx.Dx = NewDx;
  WorldTx.Dy = NewDy;

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_EMF::parse_SetWorldTransform(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetWorldTransform() ");fflush(log);
#endif
static const char ObjName[] = "!SETWORLDTRANSFORM";

  if(EmfRec.Size < 8+6*4)
    {
    strcpy(ObjType,ObjName);
    return;
    }

  RdFLOAT_LoEnd(&WorldTx.M11,wpd);
  RdFLOAT_LoEnd(&WorldTx.M12,wpd);
  RdFLOAT_LoEnd(&WorldTx.M21,wpd);
  RdFLOAT_LoEnd(&WorldTx.M22,wpd);
  RdFLOAT_LoEnd(&WorldTx.Dx,wpd);
  RdFLOAT_LoEnd(&WorldTx.Dy,wpd);

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_EMF::parse_MoveToEX(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_MoveToEx() ");fflush(log);
#endif
static const char ObjName[] = "!MOVETOEX";

  if(EmfRec.Size >= 8+8)
  {
    Rd_dword(wpd, (uint32_t*)&PositionX);
    Rd_dword(wpd, (uint32_t*)&PositionY);
    snprintf(ObjType, sizeof(ObjType),"%s(%d;%d)", ObjName+1, PositionX, PositionY);
  }
  else
    strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_LineTo(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_LineTo() ");fflush(log);
#endif
static const char ObjName[] = "!LINETO";

  if(EmfRec.Size >= 8+8)
  {
    int32_t LineX, LineY;

    Rd_dword(wpd, (uint32_t*)&LineX);
    Rd_dword(wpd, (uint32_t*)&LineY);

    float *Points = (float*)malloc(4*sizeof(float));
    if(Points!=NULL)
    {
      const float Scale = GetScale2PSU((TMapMode)MapMode);

      Points[0] = PositionX;
      Points[1] = PositionY;
      Points[2] = LineX;
      Points[3] = LineY;
      VectorLine *pVecLine = new VectorLine(Points, 2);
      pVecLine->Transform(WorldTx);
      UpdateBBox(bbx, 0, Points[0], Points[1], Points[3]-Points[0], Points[2]-Points[1]); // Update BBox after world transform.
      for(int i=0; i<4; i++) Points[i]*=Scale;

      memcpy(&pVecLine->LineColor, &PSS.LineColor, sizeof(PSS.LineColor));
      pVecLine->Close = false;
      VectList.AddObject(pVecLine); pVecLine=NULL;

      snprintf(ObjType, sizeof(ObjType),"%s(%d;%d)", ObjName+1, LineX, LineY);
      return;
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_Polygon16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_Polygon16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYGON16";
  int32_t Bounds[4];
  uint32_t Count;  

  if(EmfRec.Size < 20+8)
  {
POLY_FAIL:
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_dword(wpd, (uint32_t*)&Bounds[0]);	//LeftRect
  Rd_dword(wpd, (uint32_t*)&Bounds[1]);	//TopRect
  Rd_dword(wpd, (uint32_t*)&Bounds[2]);	//RightRect
  Rd_dword(wpd, (uint32_t*)&Bounds[3]);	//BottomRect

  Rd_dword(wpd, &Count);
  if(Count > (EmfRec.Size - 28)/4)
    Count = (EmfRec.Size - 28)/4;
  float *Points = LoadPoints(this, Count);
  if(Points==NULL)  goto POLY_FAIL;
  PositionX = Points[2*Count-2];
  PositionY = Points[2*Count-1];
  FixBoundingBox(Points, Count, bbx,&WorldTx);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  for(unsigned i=0; i<2*Count; i++)
  {
    Points[i] *= Scale;		// scale both x & y
  }

  VectorPolygon *pVecPoly = new VectorPolygon(Points, Count);
  Points = NULL;
  pVecPoly->AttribFromPSS(PSS);  
  pVecPoly->Close = true;  
  VectList.AddObject(pVecPoly); pVecPoly=NULL;

  strcpy(ObjType,ObjName+1);
  return;
}


void TconvertedPass1_EMF::parse_PolyLine16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyLine16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYLINE16";

  if(EmfRec.Size >= 8+20)
  {
    int32_t Bound1, Bound2, Bound3, Bound4;
    uint32_t Count;

    Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
    Rd_dword(wpd, &Count);
    if(Count > (EmfRec.Size-28) /4)
      Count = (EmfRec.Size-28) / 4;
    if(Count>0)
    {
      float *Points = LoadPoints(this, Count);
      if(Points!=NULL)
      {
        PositionX = Points[2*Count];
        PositionY = Points[2*Count+1];
        FixBoundingBox(Points, Count, bbx, &WorldTx, PSS.LineWidth);

        const float Scale = GetScale2PSU((TMapMode)MapMode);
        for(unsigned i=0; i<2*Count; i++)
        {
          Points[i] *= Scale;		// scale both x & y
        }

        VectorPolygon *pVecPoly = new VectorPolygon(Points,Count);
        Points = NULL;

        pVecPoly->AttribFromPSS(PSS);
        pVecPoly->Close = false;
        //pVecPoly->Transform(WorldTx);
        VectList.AddObject(pVecPoly); pVecPoly=NULL;

        strcpy(ObjType,ObjName+1);
        return;
      }
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_PolyLineTo16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyLineTo16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYLINETO16";

  if(EmfRec.Size >= 8+20)
  {
    int32_t Bound1, Bound2, Bound3, Bound4;
    uint32_t Count;

    Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
    Rd_dword(wpd, &Count);
    if(Count > (EmfRec.Size-28) /4)
      Count = (EmfRec.Size-28) / 4;
    if(Count>0)
    {
      float *Points = (float*)calloc(2*(Count+1),sizeof(float));
      if(Points!=NULL)
      {
        Points[0] = PositionX;
        Points[1] = PositionY;
        LoadPoints(this, Count, false, Points+2);
        PositionX = Points[2*Count];
        PositionY = Points[2*Count + 1];
        Count++;
        FixBoundingBox(Points, Count, bbx,&WorldTx,PSS.LineWidth);

        const float Scale = GetScale2PSU((TMapMode)MapMode);
        for(unsigned i=0; i<2*Count; i++)
        {
          Points[i] *= Scale;		// scale both x & y
        }

        VectorPolygon *pVecPoly = new VectorPolygon(Points,Count);
        Points = NULL;

        pVecPoly->AttribFromPSS(PSS);
        pVecPoly->Close = false;
        //pVecPoly->Transform(WorldTx);
        VectList.AddObject(pVecPoly); pVecPoly=NULL;

        strcpy(ObjType,ObjName+1);
        return;
      }
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_PolyBezier16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyBezier16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYBEZIER16";

  if(EmfRec.Size >= 8+20)
  {
    int32_t Bound1, Bound2, Bound3, Bound4;
    uint32_t Count;

    Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
    Rd_dword(wpd, &Count);
    if(Count > (EmfRec.Size-28) /4)
      Count = (EmfRec.Size-28) / 4;
    if(Count>0)
    {
      float *Points = LoadPoints(this, Count);
      if(Points!=NULL)
      {
        PositionX = Points[2*Count - 2];
        PositionY = Points[2*Count - 1];
        FixBoundingBox(Points, Count, bbx, &WorldTx);
        const float Scale = GetScale2PSU((TMapMode)MapMode);
        for(unsigned i=0; i<2*Count; i++)
        {
          Points[i] *= Scale;		// scale both x & y
        }

        VectorCurve *pVecCurve = new VectorCurve(Points,Count);
        Points = NULL;

        pVecCurve->AttribFromPSS(PSS);
        pVecCurve->Close = false;
        //pVecCurve->Transform(WorldTx);
        VectList.AddObject(pVecCurve); pVecCurve=NULL;

        strcpy(ObjType,ObjName+1);
        return;
      }
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_PolyBezierTo16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyBezierTo16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYBEZIERTO16";

  if(EmfRec.Size >= 8+20)
  {
    int32_t Bound1, Bound2, Bound3, Bound4;
    uint32_t Count;

    Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
    Rd_dword(wpd, &Count);

    if(Count > (EmfRec.Size-28) /4)
      Count = (EmfRec.Size-28) / 4;
    if(Count>0)
    {
      float *Points = (float*)calloc(2*(Count+1),sizeof(float));
      if(Points!=NULL)
      {
        Points[0] = PositionX;
        Points[1] = PositionY;
        LoadPoints(this, Count, false, Points+2);
        PositionX = Points[2*Count];
        PositionY = Points[2*Count + 1];
        Count++;
        FixBoundingBox(Points, Count, bbx,&WorldTx,PSS.LineWidth);

        const float Scale = GetScale2PSU((TMapMode)MapMode);
        for(unsigned i=0; i<2*Count; i++)
        {
          Points[i] *= Scale;		// scale both x & y
        }

        VectorCurve *pVecCurve = new VectorCurve(Points,Count);
        Points = NULL;

        pVecCurve->AttribFromPSS(PSS);  
        pVecCurve->Close = false;
        //pVecCurve->Transform(WorldTx);
        VectList.AddObject(pVecCurve); pVecCurve=NULL;

        strcpy(ObjType,ObjName+1);
        return;
      }
    }
  }

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_EMF::parse_PolyPolygon16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyPolygon16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYPOLYGON16";
  int32_t Bounds[4];
  uint32_t NumberOfPolygons;
  uint32_t TotalPolygonCount;
  unsigned i;

  if(EmfRec.Size < 20+8)
  {
POLY_FAIL:
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_dword(wpd, (uint32_t*)&Bounds[0]);	//LeftRect
  Rd_dword(wpd, (uint32_t*)&Bounds[1]);	//TopRect
  Rd_dword(wpd, (uint32_t*)&Bounds[2]);	//RightRect
  Rd_dword(wpd, (uint32_t*)&Bounds[3]);	//BottomRect

  Rd_dword(wpd, &NumberOfPolygons);
  Rd_dword(wpd, &TotalPolygonCount);

  uint32_t *PolypolygonPointCount = (uint32_t*)malloc(sizeof(uint32_t)*NumberOfPolygons);
  if(PolypolygonPointCount==NULL) goto POLY_FAIL;

  for(i=0; i<NumberOfPolygons; i++)
      Rd_dword(wpd, &PolypolygonPointCount[i]);

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  for(i=0; i<NumberOfPolygons; i++)
    if(PolypolygonPointCount[i]>0)
      {
        float *Points = LoadPoints(this, PolypolygonPointCount[i]);
        if(Points==NULL) break;
        FixBoundingBox(Points, PolypolygonPointCount[i], bbx, &WorldTx, PSS.LineWidth);

        for(unsigned j=0; j<2*PolypolygonPointCount[i]; j++)
        {
          Points[j] *= Scale;		// scale both x & y
        }

        VectorPolygon *pVecPoly = new VectorPolygon(Points, PolypolygonPointCount[i]);
        Points = NULL;
        pVecPoly->AttribFromPSS(PSS);  
        pVecPoly->Close = true;
        //pVecPoly->Transform(WorldTx);
        VectList.AddObject(pVecPoly); pVecPoly=NULL;
      }

  strcpy(ObjType,ObjName+1);
  return;
}


void TconvertedPass1_EMF::parse_PolyPolyLine16(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_PolyPolyLine16() ");fflush(log);
#endif
static const char ObjName[] = "!POLYPOLYLINE16";
unsigned i;

  if(EmfRec.Size >= 8+24)
  {
    int32_t Bound1, Bound2, Bound3, Bound4;
    uint32_t NumberOfPolylines;
    uint32_t TotalPointCount;
    
    Rd_dword(wpd,(uint32_t*)&Bound1); Rd_dword(wpd,(uint32_t*)&Bound2); Rd_dword(wpd,(uint32_t*)&Bound3); Rd_dword(wpd,(uint32_t*)&Bound4);
    Rd_dword(wpd, &NumberOfPolylines);
    Rd_dword(wpd, &TotalPointCount);

    uint32_t *PolylinePointCount = (uint32_t*)malloc(sizeof(uint32_t)*NumberOfPolylines);
    if(PolylinePointCount==NULL) goto POLY_FAIL;

    for(i=0; i<NumberOfPolylines; i++)
        Rd_dword(wpd, &PolylinePointCount[i]);

    const float Scale = GetScale2PSU((TMapMode)MapMode);
    for(i=0; i<NumberOfPolylines; i++)
      if(PolylinePointCount[i]>0)
      {
        float *Points = LoadPoints(this, PolylinePointCount[i]);
        if(Points==NULL) break;		// PolylinePointCount must be deallocated
        FixBoundingBox(Points, PolylinePointCount[i], bbx, &WorldTx);

        for(unsigned j=0; j<2*PolylinePointCount[i]; j++)
        {
          Points[j] *= Scale;		// scale both x & y
        }

        VectorPolygon *pVecPoly = new VectorPolygon(Points,PolylinePointCount[i]);
        Points = NULL;

        pVecPoly->AttribFromPSS(PSS);
        pVecPoly->Close = false;
        //pVecPoly->Transform(WorldTx);
        VectList.AddObject(pVecPoly); pVecPoly=NULL;

        strcpy(ObjType,ObjName+1);
      }

    free(PolylinePointCount);
    return;
  }

POLY_FAIL:
  strcpy(ObjType,ObjName);
}





void TconvertedPass1_EMF::parse_Rectangle(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_Rectangle() ");fflush(log);
#endif
static const char ObjName[] = "!RECTANGLE";

  if(EmfRec.Size >= 8+16)
  {
    int32_t	BottomRect, TopRect, RightRect, LeftRect;

    Rd_dword(wpd, (uint32_t*)&LeftRect);
    Rd_dword(wpd, (uint32_t*)&TopRect);
    Rd_dword(wpd, (uint32_t*)&RightRect);
    Rd_dword(wpd, (uint32_t*)&BottomRect);

    UpdateBBox(bbx, 0, LeftRect, BottomRect, RightRect-LeftRect, TopRect-BottomRect);

    const float Scale = GetScale2PSU((TMapMode)MapMode);

    VectorRectangle *pVectRec = new VectorRectangle(Scale*BottomRect, Scale*TopRect, Scale*LeftRect, Scale*RightRect);
    pVectRec->AttribFromPSS(PSS);
    pVectRec->Transform(WorldTx);
    VectList.AddObject(pVectRec);

    strcpy(ObjType,ObjName+1);
    return;
  }

  strcpy(ObjType,ObjName);
}

 typedef  enum  
 { 
   WHITE_BRUSH = 0x0000000,
   LTGRAY_BRUSH = 0x0000001, 
   GRAY_BRUSH = 0x0000002, 
   DKGRAY_BRUSH = 0x0000003, 
   BLACK_BRUSH = 0x0000004, 
   NULL_BRUSH = 0x0000005, 
   WHITE_PEN = 0x0000006, 
   BLACK_PEN = 0x0000007, 
   NULL_PEN = 0x0000008, 
   OEM_FIXED_FONT = 0x000000A, 
   ANSI_FIXED_FONT = 0x000000B, 
   ANSI_VAR_FONT = 0x000000C, 
   SYSTEM_FONT = 0x000000D, 
   DEVICE_DEFAULT_FONT = 0x000000E, 
   DEFAULT_PALETTE = 0x000000F, 
   SYSTEM_FIXED_FONT = 0x0000010, 
   DEFAULT_GUI_FONT = 0x0000011, 
   DC_BRUSH = 0x0000012, 
   DC_PEN = 0x0000013 
 } EMF_StockObject;


void TconvertedPass1_EMF::parse_SelectObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SelectObject() ");fflush(log);
#endif
uint32_t ObjectIndex;
static const char ObjName[] = "!SELECTOBJECT";

  if(EmfRec.Size < 8+4)
  {
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_dword(wpd, &ObjectIndex);

  if(ObjectIndex >= 0x80000000)		// stock objects.
  {
    switch(ObjectIndex & 0x7FFFFFFF)
    {
      case WHITE_BRUSH:
            PSS.FillColor.Red = PSS.FillColor.Green = PSS.FillColor.Blue  = 255;
	    PSS.FillPattern = FILL_SOLID;
	    break;
      case LTGRAY_BRUSH:
	    PSS.FillColor.Red = PSS.FillColor.Green = PSS.FillColor.Blue  = 191;
	    PSS.FillPattern = FILL_SOLID;
	    break;
      case GRAY_BRUSH:
            PSS.FillColor.Red = PSS.FillColor.Green = PSS.FillColor.Blue  = 127;
	    PSS.FillPattern = FILL_SOLID;
	    break;
      case DKGRAY_BRUSH:
            PSS.FillColor.Red = PSS.FillColor.Green = PSS.FillColor.Blue  = 63;
	    PSS.FillPattern = FILL_SOLID;
	    break;
      case BLACK_BRUSH:
            PSS.FillColor.Red = PSS.FillColor.Green = PSS.FillColor.Blue  = 0;
	    PSS.FillPattern = FILL_SOLID;
	    break;
      case NULL_BRUSH:
	    PSS.FillPattern = FILL_NONE;
	    break; 
      case WHITE_PEN:
            PSS.LineColor.Red = PSS.LineColor.Green = PSS.LineColor.Blue  = 255;
	    PSS.LineStyle = 1;
	    break;
      case BLACK_PEN:
	    PSS.LineColor.Red = PSS.LineColor.Green = PSS.LineColor.Blue  = 0;
	    PSS.LineStyle = 1;
	    break;
      case NULL_PEN:
	    PSS.LineStyle = 0;
	    break;
      default:
	    snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName, ObjectIndex);
	    return;
    }
    snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName+1, ObjectIndex);    
  }
  else
  {
    if(ObjectIndex == 0) goto OBJECT_DEFFECT;
    const VectorAttribute *vecAttr = ObjTab.GetObjectAt(ObjectIndex-1);
    if(vecAttr==NULL) goto OBJECT_DEFFECT;
    
    vecAttr->prepExport(&PSS);
    ConvertCpg = PSS.ConvertCpg;

    snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);
  }
  return;

OBJECT_DEFFECT:
  snprintf(ObjType, sizeof(ObjType),"%s(%u)",ObjName,ObjectIndex);
  return;
}


void TconvertedPass1_EMF::parse_SelectPalette(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SelectPalette() ");fflush(log);
#endif
uint32_t ObjectIndex;
static const char ObjName[] = "!SELECTPALETTE";

  if(EmfRec.Size < 8+4)
  {
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_dword(wpd, &ObjectIndex);

  if(ObjectIndex >= 0x80000000)		// stock objects.
  {
    switch(ObjectIndex & 0x7FFFFFFF)
    {
      case DEFAULT_PALETTE:
	      PSS.AttachPalette(NULL);	/// Deallocate a current palette. Default palette is system dependent huh!
	      break;
      default:
	      snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName, ObjectIndex);
	      return;
    }
    snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName+1, ObjectIndex);    
  }
  else
  {
    if(ObjectIndex == 0) goto OBJECT_DEFFECT;
    const VectorAttribute *vecAttr = ObjTab.GetObjectAt(ObjectIndex-1);
    if(vecAttr==NULL) goto OBJECT_DEFFECT;
    if(vecAttr->getAttribType()!=ATTR_PALETTE) goto OBJECT_DEFFECT;

    vecAttr->prepExport(&PSS);
    ConvertCpg = PSS.ConvertCpg;

    snprintf(ObjType, sizeof(ObjType),"%s(%u)", ObjName+1, ObjectIndex);
  }
  return;

OBJECT_DEFFECT:
  snprintf(ObjType, sizeof(ObjType),"%s(%u)",ObjName,ObjectIndex);
  return;
}


void TconvertedPass1_EMF::parse_SetBkColor(void)
{
static const char ObjName[] = "!SETBKCOLOR";
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetBkColor() ");fflush(log);
#endif

  if(EmfRec.Size < 8+4)
  {
    strcpy(ObjType,ObjName);
    return;
  }

  uint32_t D;
  Rd_dword(wpd, &D);
  PSS.FillBackground.Red   = D & 0xFF;
  PSS.FillBackground.Green = (D>>8) & 0xFF;
  PSS.FillBackground.Blue  = (D>>16) & 0xFF;

  snprintf(ObjType, sizeof(ObjType),"%s(%6.6Xh)", ObjName+1, D);
}


void TconvertedPass1_EMF::parse_SetBkMode(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetBkMode() ");fflush(log);
#endif
static const char ObjName[] = "!SETBKMODE";
uint32_t BkMode;
  if(EmfRec.Size < 8+4)
  {
    //ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,16);
    return;
  }  
  Rd_dword(wpd, &BkMode);
  if(BkMode==2)		// Opaque
  {
    MixMode = 2;
    if(PSS.FillPattern>FILL_SOLID) PSS.FillPattern|=0x80;    
  }
  else			// Transparent
  {
    MixMode = 1;
    PSS.FillPattern&=~0x80;
  }
  snprintf(ObjType, sizeof(ObjType), "%s(%u)", ObjName+1, (unsigned)BkMode);
}


void TconvertedPass1_EMF::parse_SetPixel(VectorList &VectList)
{
static const char ObjName[] = "!SETPIXELV";
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetPixel() ");fflush(log);
#endif
int32_t x,y;
RGB_Record PixColor;

  if(EmfRec.Size < 8+8+4)
  {
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_dword(wpd, (uint32_t*)&x);
  Rd_dword(wpd, (uint32_t*)&y);
  PixColor.Red = fgetc(wpd);
  PixColor.Green = fgetc(wpd);
  PixColor.Blue = fgetc(wpd);
  fseek(wpd, 1, SEEK_CUR);

  UpdateBBox(bbx, 0, x-0.5f, y-0.5f, 1, 1);

  const float Scale = GetScale2PSU((TMapMode)MapMode);

  VectorRectangle *pVectRec = new VectorRectangle(Scale*(y-0.5f), Scale*(y+0.5f), Scale*(x-0.5f), Scale*(x+0.5f));
  pVectRec->AttribFromPSS(PSS);
  pVectRec->FillColor = PixColor;
  pVectRec->BrushStyle = FILL_SOLID;
  pVectRec->LineStyle = 0;
  pVectRec->Transform(WorldTx);
  VectList.AddObject(pVectRec);  

  strcpy(ObjType,ObjName+1);

  uint32_t D;
  Rd_dword(wpd, &D);
  PSS.FillBackground.Red   = D & 0xFF;
  PSS.FillBackground.Green = (D>>8) & 0xFF;
  PSS.FillBackground.Blue  = (D>>16) & 0xFF;

  snprintf(ObjType, sizeof(ObjType),"%s(%6.6Xh)", ObjName+1, D);
}


void TconvertedPass1_EMF::parse_SetPolyFillMode(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetPolyFillMode() ");fflush(log);
#endif
static const char ObjName[] = "!SETPOLYFILLMODE";
uint32_t PolygonFillMode;
  if(EmfRec.Size < 8+4)
  {
    //ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,16);
    return;
  }  
  Rd_dword(wpd, &PolygonFillMode);
  PSS.PolyFillMode = (PolygonFillMode==1) ? 1 : 0;
  snprintf(ObjType, sizeof(ObjType), "%s(%u)", ObjName+1, (unsigned)PolygonFillMode);
}


void TconvertedPass1_EMF::parse_SetTextAlign(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetTextAlign() ");fflush(log);
#endif
uint32_t TextAlignmentMode;
static const char ObjName[] = "!SetTextAlign";

  if(EmfRec.Size < 8+4)
  {
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }
  
  Rd_dword(wpd, &TextAlignmentMode);

  PSS.TextAlign = TextAlignmentMode & 0xFF;
 
  snprintf(ObjType, sizeof(ObjType),"%s(%Xh)", ObjName+1, TextAlignmentMode);
}


void TconvertedPass1_EMF::parse_SaveDC(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SaveDC() ");fflush(log);
#endif
static const char ObjName[] = "!SAVEDC";

  if(EmfRec.Size < 8)
  {
    strcpy(ObjType,ObjName);
    return;
  }

  PushDC();
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_EMF::parse_RestoreDC(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_RestoreDC() ");fflush(log);
#endif
static const char ObjName[] = "!RESTOREDC";

  if(EmfRec.Size < 8)
  {
    strcpy(ObjType,ObjName);
    return;
  }

  PopDC();
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_EMF::parse_SetTextColor(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_SetTextColor() ");fflush(log);
#endif
static const char ObjName[] = "!SETTEXTCOLOR";

  if(EmfRec.Size >= 8+4)
  {
    uint32_t D;
    Rd_dword(wpd, &D);
    PSS.TextColor.Red   = D & 0xFF;
    PSS.TextColor.Green = (D>>8) & 0xFF;
    PSS.TextColor.Blue  = (D>>16) & 0xFF;
    snprintf(ObjType, sizeof(ObjType),"%s(%6.6Xh)", ObjName+1, D);    
    return;
  }

  strcpy(ObjType,ObjName);
}


/** This function extracts bitmap from a EMF stream. */
void EMF_STRETCHDIB(Image &Img, FILE *f, uint32_t ParamFilePos)
{
uint32_t BIH_biSize;

  fseek(f,ParamFilePos,SEEK_SET);
  if(RdDWORD_LoEnd(&BIH_biSize,f)!=4)
  {	// @TODO: Possible PNG or JPG contents.
    return;
  }

  if(BIH_biSize!=40) return;

  fseek(f,ParamFilePos,SEEK_SET);
  LoadBmpStream(Img,f,0);
}


void TconvertedPass1_EMF::parse_StretchDIBits(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_EMF::parse_StretchDIBits() ");fflush(log);
#endif
static const char ObjName[] = "!STRETCHDIBITS";
int32_t Bounds[4];
int32_t xDest, yDest, xSrc, ySrc, cxSrc, cySrc;
uint32_t offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc;
uint32_t UsageSrc, BitBltRasterOperation;
int32_t cxDest, cyDest;
Image *pImg = &Img;

  //CrackObject(this, ActualPos+EmfRec.Size);

  if(EmfRec.Size < 8+18*4)
  {
ObjectCorrupted:
    //ReportCorruptedObj(ObjName+1);
    strcpy(ObjType,ObjName);
    return;
  }

  Rd_dword(wpd, (uint32_t*)&Bounds[0]);	//LeftRect
  Rd_dword(wpd, (uint32_t*)&Bounds[1]);	//TopRect
  Rd_dword(wpd, (uint32_t*)&Bounds[2]);	//RightRect
  Rd_dword(wpd, (uint32_t*)&Bounds[3]);	//BottomRect

  Rd_dword(wpd, (uint32_t*)&xDest);
  Rd_dword(wpd, (uint32_t*)&yDest);
  Rd_dword(wpd, (uint32_t*)&xSrc);
  Rd_dword(wpd, (uint32_t*)&ySrc);
  Rd_dword(wpd, (uint32_t*)&cxSrc);
  Rd_dword(wpd, (uint32_t*)&cySrc);

  Rd_dword(wpd, &offBmiSrc);
  Rd_dword(wpd, &cbBmiSrc);
  Rd_dword(wpd, &offBitsSrc);
  Rd_dword(wpd, &cbBitsSrc);
  Rd_dword(wpd, &UsageSrc);
  Rd_dword(wpd, &BitBltRasterOperation);

  Rd_dword(wpd, (uint32_t*)&cxDest);
  Rd_dword(wpd, (uint32_t*)&cyDest);  

  if(EmfRec.Size < offBmiSrc) goto ObjectCorrupted;
  offBmiSrc = ftell(wpd) + offBmiSrc - (8+18*4);

	// Rewind to the last frame.
  while(pImg->Next!=NULL)
        pImg = pImg->Next;

  if(pImg->Raster==NULL || pImg->Raster->Data2D==NULL)
  {
    Image Img2;
    EMF_STRETCHDIB(Img2, wpd, offBmiSrc);
    if(Img2.Raster != NULL)
    {
      VectorRaster *vr;
      if(fabs(Img2.RotAngle) > 1e-5)
        vr = new VectorRaster(xDest+cxDest/2, yDest+cyDest/2, cxDest, cyDest, Img2.RotAngle);
      else
        vr = new VectorRaster(yDest, yDest+cyDest, xDest+cxDest, xDest);
      vr->AttachRaster(Img2.Raster);
      if(Img2.Palette != NULL)
          vr->AttachPalette(Img2.Palette);
      else
      {
	if(pImg->Palette==NULL)
	    vr->AttachPalette(PSS.pPalette);
	else
	    vr->AttachPalette(Img2.Palette);
      }
      vr->Transform(WorldTx);

      float dx = vr->RightCenterX - vr->CenterX;
      float dy = vr->RightCenterY - vr->CenterY;
      UpdateBBox(bbx, 0, vr->CenterX-dx, vr->CenterY-dy, 2*dx, 2*dy);
      dx = vr->TopCenterX - vr->CenterX;
      dy = vr->TopCenterY - vr->CenterY;
      UpdateBBox(bbx, 0, vr->CenterX-dx, vr->CenterY-dy, 2*dx, 2*dy);

      VectList.AddObject(vr);
    }
  }
  else
  {
    Image Img2;

    EMF_STRETCHDIB(Img2, wpd, offBmiSrc);
    Img2.x = xDest;
    Img2.y = yDest;
    Img2.dx = cxDest;
    Img2.dy = cyDest;
    if(Img2.Raster!=NULL && Img2.Palette==NULL) Img2.AttachPalette(PSS.pPalette);
    //SavePictureEPS("R:\\4\\debug.eps",Img2);

    if(Img2.Raster!=NULL && Img2.Raster->Size1D>0)
    {
      if(Img2.x == pImg->x && Img2.dx == pImg->dx &&		// The x size must match
         Img2.Raster->Size1D==pImg->Raster->Size1D && Img2.Raster->Size2D==1 && 
         Img2.Raster->GetPlanes()==pImg->Raster->GetPlanes())
      {
        void **NewBlock = (void**)realloc(pImg->Raster->Data2D, (pImg->Raster->Size2D+1)*sizeof(void**));
        if(NewBlock!=NULL)
        {
          NewBlock[pImg->Raster->Size2D] = Img2.Raster->Data2D[0];
          Img2.Raster->Data2D[0] = NULL;
	  // Img2.Raster->Size2D must not be schrinked here, schrink will cause leak. Dtor will free empty ptr.
          pImg->Raster->Data2D = NewBlock;
          pImg->Raster->Size2D++;
          pImg->dy = Img2.y - pImg->y + Img2.dy;
        }
      }
      else
      {
        pImg->Next = new Image;
        pImg = pImg->Next;
        pImg->AttachPalette(Img2.Palette);
        pImg->AttachRaster(Img2.Raster);
        pImg->x = Img2.x;        pImg->y = Img2.y;
        pImg->dx = Img2.dx;      pImg->dy = Img2.dy;
        pImg->RotAngle = Img2.RotAngle;
      }
    }
  }
  strcpy(ObjType, ObjName+1);
}


int TconvertedPass1_EMF::LoadImageEMF(void)
{
EMFHEAD EmfHead;
  if(!LoadEmfRecord(wpd, EmfRec)) return -1;
  ActualPos = EmfRec.ParamFilePos;
  if(EmfRec.Type!=1 || EmfRec.Size<80) return -2;

  if(LoadEMFHeader(wpd,EmfHead)!=80) return -3;
  if(EmfHead.Signature!=0x464D4520) return -4;

  ConvertUnicode = GetTranslator("unicodeTOinternal");
  fseek(wpd, EmfRec.ParamFilePos+EmfRec.Size, SEEK_SET);

  VectorList VectList;
  while(!feof(wpd) && LoadEmfRecord(wpd,EmfRec))
  {
    switch(EmfRec.Type)
    {
      case EMR_HEADER:		strcpy(ObjType,"!HEADER"); break;	// 0x00000001
      case EMR_POLYBEZIER:	strcpy(ObjType,"!POLYBEZIER"); PleaseReport("POLYBEZIER"); break;	// 0x00000002
      case EMR_POLYGON:		strcpy(ObjType,"!POLYGON"); PleaseReport("POLYGON"); break;	// 0x00000003
      case EMR_POLYLINE:	strcpy(ObjType,"!POLYLINE"); PleaseReport("POLYLINE"); break;	// 0x00000004
      case EMR_POLYBEZIERTO:	strcpy(ObjType,"!POLYBEZIERTO"); PleaseReport("POLYBEZIERTO"); break;	// 0x00000005
      case EMR_POLYLINETO:	strcpy(ObjType,"!POLYLINETO"); PleaseReport("POLYLINETO"); break;	// 0x00000006
      case EMR_POLYPOLYLINE:	strcpy(ObjType,"!POLYPOLYLINE"); PleaseReport("POLYPOLYLINE"); break;	// 0x00000007
      case EMR_POLYPOLYGON:	strcpy(ObjType,"!POLYPOLYGON"); PleaseReport("POLYPOLYGON"); break;	// 0x00000008
      case EMR_SETWINDOWEXTEX:	strcpy(ObjType,"!SETWINDOWEXTEX"); break;	// 0x00000009
      case EMR_SETWINDOWORGEX:	strcpy(ObjType,"!SETWINDOWORGEX"); break;	// 0x0000000A
      case EMR_SETVIEWPORTEXTEX:strcpy(ObjType,"!SETVIEWPORTEXTEX"); break;	// 0x0000000B
      case EMR_SETVIEWPORTORGEX:strcpy(ObjType,"!SETVIEWPORTORGEX"); break;	// 0x0000000C
      case EMR_SETBRUSHORGEX:	strcpy(ObjType,"!SETBRUSHORGEX"); break;	// 0x0000000D
      case EMR_EOF: strcpy(ObjType,"!EOF"); break;	// 0x0000000E
      case EMR_SETPIXELV:	parse_SetPixel(VectList); break;	// 0x0000000F 
      case EMR_SETMAPPERFLAGS:	strcpy(ObjType,"!SETMAPPERFLAGS"); break;	// 0x00000010
      case EMR_SETMAPMODE:	strcpy(ObjType,"!SETMAPMODE"); break;	// 0x00000011
      case EMR_SETBKMODE:	parse_SetBkMode(); break;	// 0x00000012
      case EMR_SETPOLYFILLMODE:	parse_SetPolyFillMode(); break;	// 0x00000013
      case EMR_SETROP2: strcpy(ObjType,"!SETROP2"); break;	// 0x00000014
      case EMR_SETSTRETCHBLTMODE: strcpy(ObjType,"!SETSTRETCHBLTMODE"); break;	// 0x00000015
      case EMR_SETTEXTALIGN:	parse_SetTextAlign(); break;	// 0x00000016
      case EMR_SETCOLORADJUSTMENT: strcpy(ObjType,"!SETCOLORADJUSTMENT"); break;	// 0x00000017
      case EMR_SETTEXTCOLOR:	parse_SetTextColor(); break;	// 0x00000018
      case EMR_SETBKCOLOR:	parse_SetBkColor(); break;	// 0x00000019
      case EMR_OFFSETCLIPRGN:	strcpy(ObjType,"!OFFSETCLIPRGN"); break;	// 0x0000001A
      case EMR_MOVETOEX:	parse_MoveToEX(); break;	// 0x0000001B
      case EMR_SETMETARGN: strcpy(ObjType,"!SETMETARGN"); break;	// 0x0000001C
      case EMR_EXCLUDECLIPRECT: strcpy(ObjType,"!EXCLUDECLIPRECT"); break;	// 0x0000001D
      case EMR_INTERSECTCLIPRECT: strcpy(ObjType,"!INTERSECTCLIPRECT"); break;	// 0x0000001E
      case EMR_SCALEVIEWPORTEXTEX: strcpy(ObjType,"!SCALEVIEWPORTEXTEX"); break;	// 0x0000001F
      case EMR_SCALEWINDOWEXTEX: strcpy(ObjType,"!SCALEWINDOWEXTEX"); break;	// 0x00000020
      case EMR_SAVEDC:		parse_SaveDC(); break;			// 0x00000021
      case EMR_RESTOREDC:	parse_RestoreDC(); break;		// 0x00000022
      case EMR_SETWORLDTRANSFORM:	parse_SetWorldTransform(); break;	// 0x00000023
      case EMR_MODIFYWORLDTRANSFORM:	parse_ModifyWorldTransform(); break;	// 0x00000024
      case EMR_SELECTOBJECT:	parse_SelectObject(); break;		// 0x00000025
      case EMR_CREATEPEN:	parse_CreatePen(); break;		// 0x00000026
      case EMR_CREATEBRUSHINDIRECT: parse_CreateBrushIndirect(); break;	// 0x00000027
      case EMR_DELETEOBJECT:	parse_DeleteObject(); break;		// 0x00000028
      case EMR_ANGLEARC:	strcpy(ObjType,"!ANGLEARC"); PleaseReport("ANGLEARC"); break;	// 0x00000029
      case EMR_ELLIPSE:		parse_Ellipse(VectList); break;		// 0x0000002A
      case EMR_RECTANGLE:	parse_Rectangle(VectList); break;	// 0x0000002B
      case EMR_ROUNDRECT:	strcpy(ObjType,"!ROUNDRECT"); PleaseReport("ROUNDRECT"); break;	// 0x0000002C
      case EMR_ARC:		strcpy(ObjType,"!ARC"); PleaseReport("ARC"); break;	// 0x0000002D
      case EMR_CHORD:		strcpy(ObjType,"!CHORD"); PleaseReport("CHORD"); break;	// 0x0000002E
      case EMR_PIE:		strcpy(ObjType,"!PIE"); PleaseReport("PIE"); break;	// 0x0000002F
      case EMR_SELECTPALETTE:   parse_SelectPalette(); break;		// 0x00000030
      case EMR_CREATEPALETTE:	parse_CreatePalette(); break;		// 0x00000031
      case EMR_SETPALETTEENTRIES: strcpy(ObjType,"!SETPALETTEENTRIES"); break;	// 0x00000032
      case EMR_RESIZEPALETTE:	strcpy(ObjType,"!RESIZEPALETTE"); break;	// 0x00000033
      case EMR_REALIZEPALETTE:	strcpy(ObjType,"!REALIZEPALETTE"); break;	// 0x00000034
      case EMR_EXTFLOODFILL:	strcpy(ObjType,"!EXTFLOODFILL"); break;	// 0x00000035
      case EMR_LINETO:		parse_LineTo(VectList); break;	// 0x00000036
      case EMR_ARCTO:		strcpy(ObjType,"!ARCTO"); PleaseReport("ARCTO"); break;	// 0x00000037
      case EMR_POLYDRAW:	strcpy(ObjType,"!POLYDRAW"); PleaseReport("POLYDRAW");  break;	// 0x00000038
      case EMR_SETARCDIRECTION: strcpy(ObjType,"!SETARCDIRECTION"); break;	// 0x00000039
      case EMR_SETMITERLIMIT:	strcpy(ObjType,"!SETMITERLIMIT"); break;	// 0x0000003A
      case EMR_BEGINPATH:	strcpy(ObjType,"!BEGINPATH"); break;	// 0x0000003B
      case EMR_ENDPATH:		strcpy(ObjType,"!ENDPATH"); break;	// 0x0000003C
      case EMR_CLOSEFIGURE:	strcpy(ObjType,"!CLOSEFIGURE"); break;	// 0x0000003D
      case EMR_FILLPATH:	strcpy(ObjType,"!FILLPATH"); break;	// 0x0000003E
      case EMR_STROKEANDFILLPATH:strcpy(ObjType,"!STROKEANDFILLPATH"); break;	// 0x0000003F
      case EMR_STROKEPATH:	strcpy(ObjType,"!STROKEPATH"); break;	// 0x00000040
      case EMR_FLATTENPATH:	strcpy(ObjType,"!FLATTENPATH"); break;	// 0x00000041
      case EMR_WIDENPATH:	strcpy(ObjType,"!WIDENPATH"); break;	// 0x00000042
      case EMR_SELECTCLIPPATH:	strcpy(ObjType,"!SELECTCLIPPATH"); break;	// 0x00000043
      case EMR_ABORTPATH:	strcpy(ObjType,"!ABORTPATH"); break;	// 0x00000044
      case EMR_COMMENT:		strcpy(ObjType,"!COMMENT"); break;	// 0x00000046
      case EMR_FILLRGN:		strcpy(ObjType,"!FILLRGN"); break;	// 0x00000047
      case EMR_FRAMERGN:	strcpy(ObjType,"!FRAMERGN"); break;	// 0x00000048
      case EMR_INVERTRGN:	strcpy(ObjType,"!INVERTRGN"); break;	// 0x00000049
      case EMR_PAINTRGN:	strcpy(ObjType,"!PAINTRGN"); break;	// 0x0000004A
      case EMR_EXTSELECTCLIPRGN:strcpy(ObjType,"!EXTSELECTCLIPRGN"); break;	// 0x0000004B
      case EMR_BITBLT:		strcpy(ObjType,"!BITBLT"); break;	// 0x0000004C
      case EMR_STRETCHBLT:	strcpy(ObjType,"!STRETCHBLT"); break;	// 0x0000004D
      case EMR_MASKBLT:		strcpy(ObjType,"!MASKBLT"); break;	// 0x0000004E
      case EMR_PLGBLT:		strcpy(ObjType,"!PLGBLT"); break;	// 0x0000004F
      case EMR_SETDIBITSTODEVICE:strcpy(ObjType,"!SETDIBITSTODEVICE"); break;	// 0x00000050
      case EMR_STRETCHDIBITS:	parse_StretchDIBits(VectList); break;	// 0x00000051
      case EMR_EXTCREATEFONTINDIRECTW: parse_ExtCreateFontIndirectW(); break;	// 0x00000052
      case EMR_EXTTEXTOUTA:	strcpy(ObjType,"!EXTTEXTOUTA"); PleaseReport("EXTTEXTOUTA"); break;	// 0x00000053
      case EMR_EXTTEXTOUTW:	parse_ExtTextOutW(VectList); break;	// 0x00000054
      case EMR_POLYBEZIER16:	parse_PolyBezier16(VectList); break;	// 0x00000055
      case EMR_POLYGON16:	parse_Polygon16(VectList); break;	// 0x00000056
      case EMR_POLYLINE16:	parse_PolyLine16(VectList); break;	// 0x00000057
      case EMR_POLYBEZIERTO16:  parse_PolyBezierTo16(VectList); break;	// 0x00000058
      case EMR_POLYLINETO16:	parse_PolyLineTo16(VectList); break; break;	// 0x00000059
      case EMR_POLYPOLYLINE16:	parse_PolyPolyLine16(VectList); break;	// 0x0000005A
      case EMR_POLYPOLYGON16:	parse_PolyPolygon16(VectList); break;	// 0x0000005B
      case EMR_POLYDRAW16: strcpy(ObjType,"!POLYDRAW16"); break;	// 0x0000005C
      case EMR_CREATEMONOBRUSH: strcpy(ObjType,"!CREATEMONOBRUSH"); break;	// 0x0000005D
      case EMR_CREATEDIBPATTERNBRUSHPT: strcpy(ObjType,"!CREATEDIBPATTERNBRUSHPT"); break;	// 0x0000005E
      case EMR_EXTCREATEPEN:	parse_ExtCreatePen(); break;	// 0x0000005F
      case EMR_POLYTEXTOUTA:	strcpy(ObjType,"!POLYTEXTOUTA"); PleaseReport("POLYTEXTOUTA"); break;	// 0x00000060
      case EMR_POLYTEXTOUTW:	strcpy(ObjType,"!POLYTEXTOUTW"); PleaseReport("POLYTEXTOUTW"); break;	// 0x00000061
      case EMR_SETICMMODE:	strcpy(ObjType,"!SETICMMODE"); break;	// 0x00000062
      case EMR_CREATECOLORSPACE: strcpy(ObjType,"!CREATECOLORSPACE"); break;	// 0x00000063
      case EMR_SETCOLORSPACE:	strcpy(ObjType,"!SETCOLORSPACE"); break;	// 0x00000064
      case EMR_DELETECOLORSPACE: strcpy(ObjType,"!DELETECOLORSPACE"); break;	// 0x00000065
      case EMR_GLSRECORD: strcpy(ObjType,"!GLSRECORD"); break;	// 0x00000066
      case EMR_GLSBOUNDEDRECORD: strcpy(ObjType,"!GLSBOUNDEDRECORD"); break;	// 0x00000067
      case EMR_PIXELFORMAT:	strcpy(ObjType,"!PIXELFORMAT"); break;	// 0x00000068
      case EMR_DRAWESCAPE:	strcpy(ObjType,"!DRAWESCAPE"); break;	// 0x00000069
      case EMR_EXTESCAPE:	strcpy(ObjType,"!EXTESCAPE"); break;	// 0x0000006A
      case EMR_SMALLTEXTOUT:	strcpy(ObjType,"!SMALLTEXTOUT"); break;	// 0x0000006C
      case EMR_FORCEUFIMAPPING: strcpy(ObjType,"!FORCEUFIMAPPING"); break;	// 0x0000006D
      case EMR_NAMEDESCAPE:	strcpy(ObjType,"!NAMEDESCAPE"); break;	// 0x0000006E
      case EMR_COLORCORRECTPALETTE: strcpy(ObjType,"!COLORCORRECTPALETTE"); break;	// 0x0000006F
      case EMR_SETICMPROFILEA:	strcpy(ObjType,"!SETICMPROFILEA"); break;	// 0x00000070
      case EMR_SETICMPROFILEW:	strcpy(ObjType,"!SETICMPROFILEW"); break;	// 0x00000071
      case EMR_ALPHABLEND:	strcpy(ObjType,"!ALPHABLEND"); break;	// 0x00000072
      case EMR_SETLAYOUT:	strcpy(ObjType,"!SETLAYOUT"); break;	// 0x00000073
      case EMR_TRANSPARENTBLT:	strcpy(ObjType,"!TRANSPARENTBLT"); break;	// 0x00000074
      case EMR_GRADIENTFILL:	strcpy(ObjType,"!GRADIENTFILL"); break;	// 0x00000076
      case EMR_SETLINKEDUFIS:	strcpy(ObjType,"!SETLINKEDUFIS"); break;	// 0x00000077
      case EMR_SETTEXTJUSTIFICATION: strcpy(ObjType,"!SETTEXTJUSTIFICATION"); break;	// 0x00000078
      case EMR_COLORMATCHTOTARGETW: strcpy(ObjType,"!COLORMATCHTOTARGETW"); break;	// 0x00000079
      case EMR_CREATECOLORSPACEW: strcpy(ObjType,"!CREATECOLORSPACEW"); break;	// 0x0000007A
      default: snprintf(ObjType, sizeof(ObjType),"!Unknown#%Xh", (unsigned)EmfRec.Type);
    }
    if(ObjType[0]=='!') UnknownObjects++;
    
    if(log!=NULL)
    {   /**/
      fprintf(log, _("\n%*s{EMF func:%3Xh; size:%4lu; pos:%lXh}"),
		recursion*2,"",EmfRec.Type, (long)EmfRec.Size, (long)EmfRec.ParamFilePos);
      if(*ObjType!=0) fprintf(log," %s",ObjType);
    }

    ActualPos = EmfRec.ParamFilePos + EmfRec.Size;
    fseek(wpd, ActualPos, SEEK_SET);
  }

#ifdef _DEBUG
  //{FILE *F=fopen("O:\\temp\\40\\wmf_file.txt","wb");VectList.Dump2File(F);fclose(F);}
#endif

  const float Scale = GetScale2PSU((TMapMode)MapMode) * 25.4f / 71.0f;	// convert PSu to WPGu (quite bad).
  vFlip flipTrx(bbx.MinY, bbx.MaxY);

  Image *Img2 = &Img;
  while(Img2!=NULL)
  {      
    if(Img2->Raster!=NULL)
    {        
      if(Img2->dy < 0)
      {
        Img2->dy = -Img2->dy;
          //Img2->y += Img2->dy;
      }
      Flip2D(Img2->Raster);
      if(Img2->dx<0)
      {        
        Img2->x += Img2->dx;
        Img2->dx = -Img2->dx;
        Flip1D(Img2->Raster);
      }

      if(YExtent>=0)
          flipTrx.ApplyTransform(Img2->x, Img2->y);
      }
      //if(Img2->Vector)
      //   Img2->Vector.Transform(flipTx);
    Img2->x *= Scale;
    Img2->y *= Scale;
    Img2->dx *= Scale;
    Img2->dy *= -Scale;
    Img2 = Img2->Next;
  }

  if(YExtent>=0)
      VectList.Transform(flipTrx);

  //if(PSData.length() > 0)
  //    VectList.AddObject(new PsBlob(PSData.ExtractString()));  

  if(VectList.VectorObjects>0 && Img.VecImage==NULL)
  {    
    Img.AttachVecImg(new VectorImage(VectList,PSS));

    if(Img.dx!=0 && Img.dy!=0 && Img.Raster!=NULL)
      {
      if(Img.VecImage!=NULL)	// Move raster data to different image frame.
        {
        Img2 = &Img;
        while(Img2->Next!=NULL)
          Img2 = Img2->Next;
        Img2->Next = new Image();
        Img2 = Img2->Next;

        Img2->x =  bbx.MinX * Scale;
        Img2->y =  bbx.MinY * Scale;
        Img2->dx = (bbx.MaxX - bbx.MinX) * Scale;
        Img2->dy = (bbx.MaxY - bbx.MinY) * Scale;
        Img2->VecImage = Img.VecImage; Img.VecImage=NULL;
        }
      }
    else	// Use whole frame as bounding box.
      {      
      Img.x =  bbx.MinX * Scale;
      Img.y =  bbx.MinY * Scale;
      Img.dx = (bbx.MaxX - bbx.MinX) * Scale;
      Img.dy = (bbx.MaxY - bbx.MinY) * Scale;
      }
  }
  return 0;
}


int TconvertedPass1_EMF::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_EMF() ");fflush(log);
#endif
int RetVal;
const uint16_t CodePageBk = OutCodePage;

  if(Verbosing >= 1) printf(_("Opening Windows Enhanced MetaFile:"));

  RetVal = LoadImageEMF();
  if(RetVal<0) return RetVal;

  if(!NoConvertImage && !Img.isEmpty())
    {
    for(Image *pImg=&Img; pImg!=NULL; pImg=pImg->Next)
      if(pImg->Raster!=NULL)
        ReducePalette(pImg,256);

    string NewFilename = MergePaths(OutputDir,RelativeFigDir);
    string wpd_cut = CutFileName(wpd_filename);
    if(recursion==0 && length(wpd_cut)>0)
      NewFilename += wpd_cut + ".eps";
    else
      NewFilename += GetFullFileName(GetSomeImgName(".eps"));
    if(SavePictureEPS(NewFilename(),Img)<0)
	{
        if(err != NULL)
	  {
	  perc.Hide();
	  fprintf(err, _("\nError: Cannot save file: \"%s\"!"), NewFilename());
	  }
	return 0;
        }

    NewFilename = CutFileName(NewFilename); 	//New Filename only

    PutImageIncluder(NewFilename());

    InputPS |= 1;		//mark style as used
    Finalise_Conversion(this);
    RetVal++;
    }

  OutCodePage = CodePageBk;
  return(RetVal);
}


Image LoadPictureEMF(const char *Name)
{
TconvertedPass1_EMF ConvEMF;
size_t fsize;

  ConvEMF.err = stderr;
  ConvEMF.strip = ConvEMF.log = NULL;
  ConvEMF.flag = NormalText;
  if(Name)
  {
    ConvEMF.wpd = fopen(Name,"rb");
    if(ConvEMF.wpd)
    {
      ConvEMF.PositionX = ConvEMF.PositionY = 0;

      ConvEMF.ActualPos = ConvEMF.DocumentStart = ftell(ConvEMF.wpd);
      fsize = FileSize(ConvEMF.wpd);

      ConvEMF.LoadImageEMF();
      //ConvSvg.perc.Init(ftell(ConvSvg.wpd), fsize,_("First pass EMF:") );

      fclose(ConvEMF.wpd);
      ConvEMF.wpd = NULL;

      //ConvEMF.ProcessImage();
    }
  }
  return ConvEMF.Img;
}


#ifndef ImplementReader
#define ImplementReader(SUFFIX,EXTENSION) \
Image LoadPicture##SUFFIX(const char *Name);\
int SavePicture##SUFFIX(const char *Name,const Image &Img);\
TImageFileHandler HANDLER_##SUFFIX(EXTENSION,
#endif

ImplementReader(EMF,".EMF")
   LoadPictureEMF,
   NULL);


/*--------------------End of PASS1_WMF--------------------*/
