/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    Convert WordPerfect files into LaTeX.			      *
 * modul:       pass1wmf.cc                                                   *
 * description: Opens 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>

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

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


void WMF_STRETCHDIB(Image &Img, FILE *f, DWORD ParamFilePos);
int SavePictureEPS(const char *Name, const Image &Img);
string CutFileName(const char *FullFilename); //from wp2lfuti.cc


/// 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 
} 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  
{ 
  MM_TEXT = 0x0001, 
  MM_LOMETRIC = 0x0002, 
  MM_HIMETRIC = 0x0003, 
  MM_LOENGLISH = 0x0004, 
  MM_HIENGLISH = 0x0005, 
  MM_TWIPS = 0x0006, 
  MM_ISOTROPIC = 0x0007, 
  MM_ANISOTROPIC = 0x0008 
} TMapMode;

/// Get scalling factor for a given map mode.
/// @return	Scalling factor.
float GetScale2PSU(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;
}


/** This functor class is used for flipping image data. */
class vFlip: public AbstractTransformXY
{
public:
  vFlip(float Top, float Bottom) {TopPBottom=Top+Bottom;}

  virtual void ApplyTransform(float &x, float &y) const {y = -y+TopPBottom;}

  float TopPBottom;
};



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


class ObjectTable
{
public:
  VectorAttribute **Attributes;
  unsigned Count;

  ObjectTable() {Count=0; Attributes=NULL;}  
  ~ObjectTable();

  void AddObject(VectorAttribute *NewAttr);
  void DeleteObject(unsigned ObjectIndex);
  const VectorAttribute *GetObjectAt(unsigned ObjectIndex) const;
};


ObjectTable::~ObjectTable()
{
  if(Count>0)
  {
    if(Attributes!=NULL)
    {
      for(unsigned i=0; i<Count; i++)
      {
        delete Attributes[i]; 
        Attributes[i] = NULL;
      }
      free(Attributes);
      Attributes = NULL;
    }
    Count = 0;
  }
}


void ObjectTable::AddObject(VectorAttribute *NewAttr)
{
  if(NewAttr==NULL) return;

  if(Attributes==NULL || Count<=0)
  {
    Attributes = (VectorAttribute**)malloc(sizeof(VectorAttribute*));
    if(Attributes==NULL)
    {
      delete NewAttr;
      return;
    }
    Attributes[0] = NewAttr;
    Count = 1;
    return;
  }

  for(unsigned i=0; i<Count; i++)
  {
    if(Attributes[i]==NULL)
    {
      Attributes[i] = NewAttr;
      return;
    }
  }

  Count++;
  VectorAttribute **tmp = (VectorAttribute**)realloc(Attributes, Count*sizeof(VectorAttribute*));
  if(tmp==NULL)		// memory exhausted, but Attributes contains original array.
    {ReportNoMemory();return;}
  Attributes = tmp;
  Attributes[Count-1] = NewAttr;
}


void ObjectTable::DeleteObject(unsigned ObjectIndex)
{
  if(ObjectIndex>=Count || Attributes==NULL) return;
  delete Attributes[ObjectIndex];
  Attributes[ObjectIndex] = NULL;
}


const VectorAttribute *ObjectTable::GetObjectAt(unsigned ObjectIndex) const
{
  if(ObjectIndex>=Count || Attributes==NULL) return NULL;
  return Attributes[ObjectIndex];
}


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

class TconvertedPass1_xMF: public TconvertedPass1
{
protected:
     int NoConvertImage;

public:
     TconvertedPass1_xMF(void);

     SWORD YOffset, XOffset;
     SWORD YExtent, XExtent;
     
     Image Img;     
     WORD MapMode;     
     FloatBBox bbx;
     PS_State PSS;
     string PSData;
     ObjectTable ObjTab;
     CpTranslator *PsNativeCP;
     CpTranslator *PsNativeSym;
};


TconvertedPass1_xMF::TconvertedPass1_xMF(void)
{
  NoConvertImage = 0;  
  YOffset = XOffset = 0;
  YExtent = XExtent = 0;
  bbx.MinX=65537; bbx.MaxX=-32767; bbx.MinY=65537; bbx.MaxY=-32767;
  memset(&PSS, 0, sizeof(PSS));
  PSS.FillPattern = 1;
  PSS.FontSizeW = PSS.FontSize = 1100/2.66;		// Default font size [mm]
  MapMode = MM_ANISOTROPIC;
  PsNativeCP = GetTranslator("internalTOcp1276");
  PsNativeSym = GetTranslator("internalTOsymbol");  
}



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

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);

     SWORD PositionX, PositionY;
     _StandardMetaRecord WmfRec;

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

     void parse_CreateBrushIndirect(void);
     void parse_CreatePenIndirect(void);
     void parse_DeleteObject(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_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_SelectObject(void);
     void parse_SetBkColor(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
{
  WORD  FileType;       ///< Type of metafile (0=memory, 1=disk).
  WORD  HeaderSize;     ///< Size of header in WORDS (always 9).
  WORD  Version;        ///< Version of Microsoft Windows used.
  DWORD FileSize;       ///< Total size of the metafile in WORDs.
  WORD  NumOfObjects;   ///< Number of objects in the file.
  DWORD MaxRecordSize;  ///< The size of largest record in WORDs.
  WORD  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\"."), ObjName);
}


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

  if(WmfRec.Size-3<sizeof(MathType)/sizeof(WORD)) goto NoMTEF;

  fseek(wpd,WmfRec.ParamFilePos,SEEK_SET);
  for(Size=0; Size<sizeof(MathType)/sizeof(WORD); Size++)
      {
      Rd_word(wpd,&w);
      if(w!=MathType[Size]) goto NoMTEF;
      }

  strcpy(ObjType+6," MTEF");
  if(err != NULL)
    {
    perc.Hide();
    fprintf(err, _("\nNote: MTEF formulas inside WMF are not converted yet."));
    }

  strcpy(ObjType,"!Escape - MTEF");
  return;


NoMTEF:
  strcpy(ObjType,"!Escape");
}


/** 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(WORD 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_Ellipse(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_Ellipse() ");fflush(log);
#endif
static const char ObjName[] = "!Ellipse";
SWORD BottomRect, TopRect, RightRect, LeftRect;

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

  Rd_word(wpd, (WORD*)&BottomRect);
  Rd_word(wpd, (WORD*)&RightRect);
  Rd_word(wpd, (WORD*)&TopRect);
  Rd_word(wpd, (WORD*)&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";
SWORD	XRadial1, YRadial1, XRadial2, YRadial2;
SWORD	BottomRect, RightRect, TopRect, LeftRect;

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

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

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


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

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

  Rd_word(wpd, (WORD*)&Height);
  Rd_word(wpd, &Width);
  Rd_word(wpd, &Escapement);
  Rd_word(wpd, (WORD*)&PSS.FontOrientation10);  
  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 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 SHIFTJIS_CHARSET:
    case HANGUL_CHARSET:
    case JOHAB_CHARSET:
    case GB2312_CHARSET:
    case CHINESEBIG5_CHARSET:    
    case TURKISH_CHARSET:
    case VIETNAMESE_CHARSET:
    case HEBREW_CHARSET:
    case ARABIC_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);

  sprintf(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_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, (WORD*)&PositionY);
    Rd_word(wpd, (WORD*)&PositionX);
    sprintf(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";
WORD BrushStyle;
  if(!CheckWmfRecSz(3))
    {
      ReportCorruptedObj(ObjName+1);
      strcpy(ObjType, ObjName);
      return;
    }
   
  Rd_word(wpd, &BrushStyle);
  switch(BrushStyle)
  {    
    case BS_SOLID:	PSS.FillPattern=1; break;
    case BS_HATCHED:	PSS.FillPattern=2; break;
    case BS_NULL:
    default:		PSS.FillPattern=0; break;
  }

  PSS.FillColor.Red   = fgetc(wpd);
  PSS.FillColor.Green = fgetc(wpd);
  PSS.FillColor.Blue  = fgetc(wpd);  

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


void TconvertedPass1_WMF::parse_CreatePenIndirect(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_CreatePenIndirect() ");fflush(log);
#endif
static const char ObjName[] = "!CreatePenIndirect";
WORD 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, 
  }

  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));
  strcpy(ObjType, ObjName+1);
}


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

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

  Rd_word(wpd, (WORD*)&BottomRect);
  Rd_word(wpd, (WORD*)&RightRect);
  Rd_word(wpd, (WORD*)&TopRect);
  Rd_word(wpd, (WORD*)&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_DeleteObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_DeleteObject() ");fflush(log);
#endif
WORD ObjectIndex;
static const char ObjName[] = "!DeleteObject";

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


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

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

  const VectorAttribute *vecAttr = ObjTab.GetObjectAt(ObjectIndex);
  if(vecAttr==NULL)
  {
    strcpy(ObjType,ObjName);
    return;
  }

  vecAttr->prepExport(&PSS);
  ConvertCpg = PSS.ConvertCpg;
  sprintf(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);
    }

  strcpy(ObjType,"SetBkColor");
}


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_SetWindowExt(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_SetWindowExt() ");fflush(log);
#endif
  Rd_word(wpd, (WORD*)&YExtent);
  Rd_word(wpd, (WORD*)&XExtent);
  sprintf(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, (WORD*)&YOffset);
  Rd_word(wpd, (WORD*)&XOffset);
  sprintf(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))
  {
    SWORD LineX, LineY;

    Rd_word(wpd, (WORD*)&LineY);
    Rd_word(wpd, (WORD*)&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;
      Points[2] = LineX * Scale;
      Points[3] = LineY * Scale;
      VectorLine *pVecLine = new VectorLine(Points, 2);      

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

      strcpy(ObjType,ObjName+1);
      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";
  WORD 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, bbx, NULL);
  if(Points==NULL)  goto POLY_FAIL;  

  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);
  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";
WORD PolyCount;
WORD *PolySizes;
WORD 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 = (WORD*)malloc(PolyCount*sizeof(WORD));
  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], bbx, NULL);
    if(Points==NULL)
        {free(PolySizes);goto POLY_FAIL;}
  
    for(int j=0; j<2*PolySizes[i]; j++)
    {
      Points[i] *= 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;  
    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";
  WORD 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, bbx, NULL);
  if(Points==NULL)  goto POLY_FAIL;

  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_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);
  sprintf(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(%u)";
WORD PolyFillMode;
  if(!CheckWmfRecSz(1))
  {
    ReportCorruptedObj(ObjName+1);
    strncpy(ObjType,ObjName,16);
    return;
  }  
  Rd_word(wpd, &PolyFillMode);
  PSS.PolyFillMode = (PolyFillMode==1) ? 1 : 0;
  sprintf(ObjType, ObjName+1, (unsigned)PolyFillMode);
}


void TconvertedPass1_WMF::parse_TextOut(VectorList &VectList)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WMF::parse_TextOut() ");fflush(log);
#endif
static const char ObjName[] = "!TextOut";
WORD StringLen;
short int LineLen, CurLineLen;
SWORD 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->FontOrientation = PSS.FontOrientation10 / 10.0f;
  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->FontOrientation = PSS.FontOrientation10 / 10.0f;
      }
    }
    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 allignment.
    fseek(wpd,1,SEEK_CUR);

  SWORD YStart;
  Rd_word(wpd, (WORD*)&YStart);
  Rd_word(wpd, (WORD*)&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";
SWORD x_pos, y_pos;
WORD fuOptions, TextLen;
short int LineLen, CurLineLen;
unsigned char c;

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

  int MaxTextLen = 2*(WmfRec.Size - 4);
  if(fuOptions & 0x0004)		// ETO_CLIPPED Rectangle provided
  {
    fseek(wpd, 8, SEEK_CUR);	//TODO: dopsat!
    MaxTextLen -= 8;
  }

  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;
  }

  const float Scale = GetScale2PSU((TMapMode)MapMode);
  TextContainer *pTextCont = new TextContainer;  
  pTextCont->PosX = Scale*x_pos;
  pTextCont->PosY = Scale*y_pos;
  pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;

  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->FontOrientation = PSS.FontOrientation10 / 10.0f;
      }
    }
    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(pTextCont->isEmpty())    
    delete pTextCont;    
  else    
    VectList.AddObject(pTextCont);    
  pTextCont=NULL;

  //if(VectList.isEmpty()) goto TEXT_OUT_FAIL;
  if(CurLineLen>LineLen) LineLen=CurLineLen;

  UpdateBBox(bbx, 0, x_pos, y_pos+0.1*mm2PSu(PSS.FontSize),		// Extend down reserved space for character.
             LineLen*mm2PSu(PSS.FontSizeW), -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);
  }
  else
  {
    Image Img2;
    WMF_STRETCHDIB(Img2, wpd, WmfRec.ParamFilePos);
    //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");
}


/*******************************************************************/
/* 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
_WindowsMetaHeader WmfHead;
int RetVal = 0;
DWORD NewPos;
const unsigned short CodePageBk = OutCodePage;
//DWORD fsize=filesize(wpd);

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

  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;

	case 0x001E: strcpy(ObjType,"!SaveDC"); break;		//?

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

	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: strcpy(ObjType,"!CreatePalette"); break;
	case 0x00F8: strcpy(ObjType,"!CreateBrush"); break;	//?

	case 0x0102: strcpy(ObjType,"!SetBkMode"); break;
	case 0x0103: parse_SetMapMode(); break;
	case 0x0104: strcpy(ObjType,"!SetROP2"); break;
	case 0x0105: strcpy(ObjType,"!SetRelabs"); break;
	case 0x0106: parse_SetPolyFillMode(); break;
	case 0x0107: strcpy(ObjType,"!SetStretchBltMode"); break;
	case 0x0108: strcpy(ObjType,"!SetTextCharExtra"); break;

	case 0x0127: strcpy(ObjType,"!RestoreDC"); break;

	case 0x012A: strcpy(ObjType,"!InvertRegion"); break;
	case 0x012B: strcpy(ObjType,"!PaintRegion"); break;
	case 0x012C: strcpy(ObjType,"!SelectClipRegion"); break;
	case 0x012D: parse_SelectObject(); break;
	case 0x012E: strcpy(ObjType,"!SetTextAlign"); break;

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

	case 0x0142: strcpy(ObjType,"!DibCreatePatternBrush"); break;

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

	case 0x01F0: parse_DeleteObject(); break;

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

	case 0x0201: parse_SetBkColor(); break;

	case 0x0209: parse_SetTextColor(); break;
	case 0x020A: strcpy(ObjType,"!SetTextJustification"); break;//?
	case 0x020B: parse_SetWindowOrg(); break;	//?
	case 0x020C: parse_SetWindowExt(); break;	//?
	case 0x020D: strcpy(ObjType,"!SetViewportOrg"); break; //?
	case 0x020E: strcpy(ObjType,"!SetViewportExt"); break; //?
	case 0x020F: strcpy(ObjType,"!OffsetWindowOrg"); break; //?

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

	case 0x0220: strcpy(ObjType,"!OffsetClipRgn"); break;

	case 0x0228: strcpy(ObjType,"!FillRegion"); break;

	case 0x0231: strcpy(ObjType,"!SetMapperFlags"); break;

	case 0x0234: strcpy(ObjType,"!SelectPalette"); break;

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

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

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

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

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

	case 0x0418: parse_Ellipse(VectList); break;
	case 0x0419: strcpy(ObjType,"!FloodFill"); break;

	case 0x041B: parse_Rectangle(VectList); break;

	case 0x041F: strcpy(ObjType,"!SetPixel"); break;

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

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

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

	case 0x0548: strcpy(ObjType,"!ExtFloodFill"); break;	

	case 0x061C: strcpy(ObjType,"!RoundRect"); break;
	case 0x061D: strcpy(ObjType,"!PatBlt"); break;

	case 0x626: wmf_ESCAPE(); break;

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

	case 0x06FE: strcpy(ObjType,"!CreateBitmap"); break;
	case 0x06FF: strcpy(ObjType,"!CreateRegion"); break;

        case 0x0817: strcpy(ObjType,"!Arc"); break;

	case 0x081A: strcpy(ObjType,"!Pie"); break;

 	case 0x0830: parse_Chord(); break;

	case 0x0922: strcpy(ObjType,"!BitBlt"); break;

	case 0x0940: strcpy(ObjType,"!DibBitblt"); break;

	case 0x0A32: parse_ExtTextOut(VectList); break;

	case 0x0B23: strcpy(ObjType,"!StretchBlt"); break;

	case 0x0B41: strcpy(ObjType,"!DibStretchBlt"); break;

	case 0x0d33: strcpy(ObjType,"!SetDibToDev"); break;

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

        default: sprintf(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<WmfRec.Size-3; i++)
	    //fprintf(log,"%X ",WmfRec->Parameters[i]);
	    {
	    WORD 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);
      }
    else
      ActualPos = ftell(wpd);
    }

    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;  
    }        
  }

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

    string NewFilename = MergePaths(OutputDir,RelativeFigDir) + 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

    if(InputPS & IMG_graphicx)	//graphicx.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\includegraphics");
      fprintf(strip,"[width=\\textwidth]");
      fprintf(strip,"{\\FigDir/%s.eps}",NewFilename() );
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
    }
    else if(InputPS & IMG_epsfig)	//epsfig.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\epsfig{file=\\FigDir/%s.eps,",NewFilename() );
      fprintf(strip,"width=\\textwidth");
      putc('}',strip);
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      }
    else if(InputPS & IMG_graphics)	//graphics.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\includegraphics");
      fprintf(strip,"{\\FigDir/%s.eps}",NewFilename() );
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      }
    else			//InputPS.sty
      {
      fprintf(strip,"\\begin{forcewidth}");
      fprintf(strip,"{\\textwidth}\n"); rownum++;
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\InputPS{\\FigDir/%s.eps}",NewFilename() );
      putc('\n',strip);  rownum++;
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      fprintf(strip,"\\end{forcewidth}\n");  rownum++;
      }

    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
{
  DWORD Key;           /* Magic number (always 9AC6CDD7h) */
  WORD  Handle;        /* Metafile HANDLE number (always 0) */
  SWORD Left;          /* Left coordinate in metafile units */
  SWORD Top;           /* Top coordinate in metafile units */
  SWORD Right;         /* Right coordinate in metafile units */
  SWORD Bottom;        /* Bottom coordinate in metafile units */
  WORD  Inch;          /* Number of metafile units per inch */
  DWORD Reserved;      /* Reserved (always 0) */
  WORD  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;

  DWORD Checksum = hdr.Key;
  Checksum ^= hdr.Handle;
  Checksum ^= hdr.Left;
  Checksum ^= hdr.Top;
  Checksum ^= hdr.Right;
  Checksum ^= 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);


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

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



class TconvertedPass1_EMF: public TconvertedPass1_xMF
{
public:
    SDWORD PositionX, PositionY;

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

    virtual int Convert_first_pass(void);

    void parse_LineTo(VectorList &VectList);
    void parse_MoveToEX(void);

    _EnhancedMetaRecord EmfRec;
};

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



typedef struct
{
  DWORD Bounds[4];	///< Rectangular inclusive-inclusive bounds in logical units of the smallest rectangle
  DWORD Frame[4];	///< Rectangular inclusive-inclusive dimensions, in .01 millimeter units, of a rectangle that surrounds the image stored in the metafile.
  DWORD Signature;
  DWORD Version;
  DWORD Bytes;
  DWORD Records;
  WORD  Handles;	///< number of graphics objects that are used during the processing of the metafile.
  WORD  Reserved;	///< An unsigned integer that MUST be 0x0000 and MUST be ignored.
  DWORD nDescription;	///< Number of characters in the array that contains the description of the metafile's contents.
  DWORD offDescription;	///< offset from the beginning of this record to the array that contains the description of the metafile's contents.
  DWORD nPalEntries;	///< Number of entries in the metafile palette.
  DWORD Device[2];	///< A SizeL object ([MS-WMF] section 2.2.2.22) that specifies the size of the reference device, in pixels. 
  DWORD 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_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, (DWORD*)&PositionX);		// @TODO: extend PositionX,Y to 32 bits.    
    Rd_dword(wpd, (DWORD*)&PositionY);    
    sprintf(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)
  {
    SDWORD LineX, LineY;

    Rd_dword(wpd, (DWORD*)&LineY);
    Rd_dword(wpd, (DWORD*)&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;
      Points[2] = LineX * Scale;
      Points[3] = LineY * Scale;
      VectorLine *pVecLine = new VectorLine(Points, 2);      

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

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

  strcpy(ObjType,ObjName);
}


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

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

  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;

  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"); break;	// 0x00000002
      case EMR_POLYGON: strcpy(ObjType,"!POLYGON"); break;	// 0x00000003
      case EMR_POLYLINE: strcpy(ObjType,"!POLYLINE"); break;	// 0x00000004
      case EMR_POLYBEZIERTO: strcpy(ObjType,"!POLYBEZIERTO"); break;	// 0x00000005
      case EMR_POLYLINETO: strcpy(ObjType,"!POLYLINETO"); break;	// 0x00000006
      case EMR_POLYPOLYLINE: strcpy(ObjType,"!POLYPOLYLINE"); break;	// 0x00000007
      case EMR_POLYPOLYGON: strcpy(ObjType,"!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: strcpy(ObjType,"!SETPIXELV"); break;	// 0x0000000F 
      case EMR_SETMAPPERFLAGS: strcpy(ObjType,"!SETMAPPERFLAGS"); break;	// 0x00000010
      case EMR_SETMAPMODE: strcpy(ObjType,"!SETMAPMODE"); break;	// 0x00000011
      case EMR_SETBKMODE: strcpy(ObjType,"!SETBKMODE"); break;	// 0x00000012
      case EMR_SETPOLYFILLMODE: strcpy(ObjType,"!SETPOLYFILLMODE"); break;	// 0x00000013
      case EMR_SETROP2: strcpy(ObjType,"!SETROP2"); break;	// 0x00000014
      case EMR_SETSTRETCHBLTMODE: strcpy(ObjType,"!SETSTRETCHBLTMODE"); break;	// 0x00000015
      case EMR_SETTEXTALIGN: strcpy(ObjType,"!SETTEXTALIGN"); break;	// 0x00000016
      case EMR_SETCOLORADJUSTMENT: strcpy(ObjType,"!SETCOLORADJUSTMENT"); break;	// 0x00000017
      case EMR_SETTEXTCOLOR: strcpy(ObjType,"!SETTEXTCOLOR"); break;	// 0x00000018
      case EMR_SETBKCOLOR: strcpy(ObjType,"!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: strcpy(ObjType,"!SAVEDC"); break;	// 0x00000021
      case EMR_RESTOREDC: strcpy(ObjType,"!RESTOREDC"); break;	// 0x00000022
      case EMR_SETWORLDTRANSFORM: strcpy(ObjType,"!SETWORLDTRANSFORM"); break;	// 0x00000023
      case EMR_MODIFYWORLDTRANSFORM: strcpy(ObjType,"!MODIFYWORLDTRANSFORM"); break;	// 0x00000024
      case EMR_SELECTOBJECT: strcpy(ObjType,"!SELECTOBJECT"); break;	// 0x00000025
      case EMR_CREATEPEN: strcpy(ObjType,"!CREATEPEN"); break;	// 0x00000026
      case EMR_CREATEBRUSHINDIRECT: strcpy(ObjType,"!CREATEBRUSHINDIRECT"); break;	// 0x00000027
      case EMR_DELETEOBJECT: strcpy(ObjType,"!DELETEOBJECT"); break;	// 0x00000028
      case EMR_ANGLEARC: strcpy(ObjType,"!ANGLEARC"); break;	// 0x00000029
      case EMR_ELLIPSE: strcpy(ObjType,"!ELLIPSE"); break;	// 0x0000002A
      case EMR_RECTANGLE: strcpy(ObjType,"!RECTANGLE"); break;	// 0x0000002B
      case EMR_ROUNDRECT: strcpy(ObjType,"!ROUNDRECT"); break;	// 0x0000002C
      case EMR_ARC: strcpy(ObjType,"!ARC"); break;	// 0x0000002D
      case EMR_CHORD: strcpy(ObjType,"!CHORD"); break;	// 0x0000002E
      case EMR_PIE: strcpy(ObjType,"!PIE"); break;	// 0x0000002F
      case EMR_SELECTPALETTE: strcpy(ObjType,"!SELECTPALETTE"); break;	// 0x00000030
      case EMR_CREATEPALETTE: strcpy(ObjType,"!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"); break;	// 0x00000037
      case EMR_POLYDRAW: strcpy(ObjType,"!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: strcpy(ObjType,"!STRETCHDIBITS"); break;	// 0x00000051
      case EMR_EXTCREATEFONTINDIRECTW: strcpy(ObjType,"!EXTCREATEFONTINDIRECTW"); break;	// 0x00000052
      case EMR_EXTTEXTOUTA: strcpy(ObjType,"!EXTTEXTOUTA"); break;	// 0x00000053
      case EMR_EXTTEXTOUTW: strcpy(ObjType,"!EXTTEXTOUTW"); break;	// 0x00000054
      case EMR_POLYBEZIER16: strcpy(ObjType,"!POLYBEZIER16"); break;	// 0x00000055
      case EMR_POLYGON16: strcpy(ObjType,"!POLYGON16"); break;	// 0x00000056
      case EMR_POLYLINE16: strcpy(ObjType,"!POLYLINE16"); break;	// 0x00000057
      case EMR_POLYBEZIERTO16: strcpy(ObjType,"!POLYBEZIERTO16"); break;	// 0x00000058
      case EMR_POLYLINETO16: strcpy(ObjType,"!POLYLINETO16"); break;	// 0x00000059
      case EMR_POLYPOLYLINE16: strcpy(ObjType,"!POLYPOLYLINE16"); break;	// 0x0000005A
      case EMR_POLYPOLYGON16: strcpy(ObjType,"!POLYPOLYGON16"); 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: strcpy(ObjType,"!EXTCREATEPEN"); break;	// 0x0000005F
      case EMR_POLYTEXTOUTA: strcpy(ObjType,"!POLYTEXTOUTA"); break;	// 0x00000060
      case EMR_POLYTEXTOUTW: strcpy(ObjType,"!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: sprintf(ObjType,"!Unknown#%X", (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)2*EmfRec.Size, (long)EmfRec.ParamFilePos);
      if(*ObjType!=0) fprintf(log," %s",ObjType);
    }

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

  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(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;  
    }        
  }

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

    string NewFilename = MergePaths(OutputDir,RelativeFigDir) + 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

    if(InputPS & IMG_graphicx)	//graphicx.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\includegraphics");
      fprintf(strip,"[width=\\textwidth]");
      fprintf(strip,"{\\FigDir/%s.eps}",NewFilename() );
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
    }
    else if(InputPS & IMG_epsfig)	//epsfig.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\epsfig{file=\\FigDir/%s.eps,",NewFilename() );
      fprintf(strip,"width=\\textwidth");
      putc('}',strip);
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      }
    else if(InputPS & IMG_graphics)	//graphics.sty
      {
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\includegraphics");
      fprintf(strip,"{\\FigDir/%s.eps}",NewFilename() );
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      }
    else			//InputPS.sty
      {
      fprintf(strip,"\\begin{forcewidth}");
      fprintf(strip,"{\\textwidth}\n"); rownum++;
      fprintf(strip," \\begin{center}");
      fprintf(strip,"\\InputPS{\\FigDir/%s.eps}",NewFilename() );
      putc('\n',strip);  rownum++;
      fprintf(strip," \\end{center}");
      putc('\n',strip);  rownum++;
      fprintf(strip,"\\end{forcewidth}\n");  rownum++;
      }

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

  OutCodePage = CodePageBk;
  return(RetVal);
}



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