/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    convert WordPerfect files into LaTeX                          *
 * modul:       images.cc                                                     *
 * description: Procedures for converting WP images into into PostScripts ones*
 *              that could be inserted into a LaTeX.                          *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

	//Atoms library
#include "stringa.h"
#include "lists.h"
#include "sets.h"

#include "wp2latex.h"
#include "cp_lib/cptran.h"

#ifdef __GNUC__
#include <unistd.h>
#endif

#ifdef _MSC_VER
 #pragma warning (disable : 4244)	/* double to float conversion - stupid warning */
 #pragma warning (disable : 4305)
#endif

#include "images/vecimage.h"
#include "images/ras_img.cc"
#include "images.h"

#define PS2WPG(x) ((x)*25.4/71)


#ifdef _REENTRANT 
 #include "jobs/jobs.h"
 class SaveEpsJob: public JobBase
 {
public:
  string FileName;
  Image Img;
  FILE *err;

  SaveEpsJob(Image &iImg, const string & iFileName, FILE *iErr=NULL)
    {Img=iImg; FileName=iFileName; err=iErr;}
  SaveEpsJob(Image &iImg, const char *iFileName, FILE *iErr=NULL)
    {Img=iImg; FileName=iFileName; err=iErr;}

  virtual void Run(void)  
  {
    if(SavePictureEPS(FileName(),Img)<0)
    {
      if(err!=NULL)		  
        fprintf(err, _("\nError: Cannot save file: \"%s\"!"), FileName());
    }
  }  
  virtual ~SaveEpsJob() {err=NULL;}
};

  /// Save EPS image from different thread.
  void SavePictureEPS_MT(const string &FILE_NAME, Image &IMAGE_OBJ, FILE *ERR_STREAM, Percentor*PPERCENTOR)
  {
    RunJob(new SaveEpsJob(IMAGE_OBJ, FILE_NAME, ERR_STREAM));    
  }

#else

 /// Save imageas EPS immediatelly.
 inline void SavePictureEPS_MT(const string &FILE_NAME, Image &IMAGE_OBJ, FILE *ERR_STREAM, Percentor*PPERCENTOR) 
 {
  if(SavePictureEPS(FILE_NAME(),IMAGE_OBJ) < 0)
  {
    if(ERR_STREAM != NULL)
    {
      PPERCENTOR->Hide();
      fprintf(ERR_STREAM, _("\nError: Cannot save file: \"%s\"!"), FILE_NAME());
    }
  }
 }

#endif


extern string wpd_filename;

#define DEFAULT_BOX_WIDTH 10	///< Use 10cm as a default width

string CutFileName(const char *FullFilename); //from wp2lfuti.cc

#ifndef FINAL
void CrackObject(TconvertedPass1 *cq, DWORD end_of_code);
#endif


#define WPGu2PSu(x) ( (float)(x)*71/(25.4*47.0) )
#define mm2PSu(x)   ( (float)(x)*71/25.4 )

static SWORD Rd_word_MMX(FILE *F,float &Min,float &Max)
{
SWORD w;
  Rd_word(F,(WORD *)&w);
  if(w<Min) Min=w;
  if(w>Max) Max=w;
  return(w);
}

typedef struct EmbeddedImg {
   DWORD Offset;
   DWORD Length;
   char *Extension;
   char *ImgName;
   struct EmbeddedImg *Next;
   }EmbeddedImg;


typedef struct {
	DWORD PS_unknown1;
	WORD PS_unknown2;
	WORD PS_unknown3;
       } WPGPSl1Record;


static void Wr_WP_DWORD(FILE *f, DWORD d, char Format=0)
{
   unsigned char b;

   if(d<0xFF && Format<1)
       {
       b=d;
       fwrite(&b, 1, 1, f);
       return;
       }

  b=0xff;
  fwrite(&b, 1, 1, f);

  if(d<0x8000 && Format<2)
       {
       b = d & 255;
       fwrite(&b, 1, 1, f);
       b = d / 256;
       fwrite(&b, 1, 1, f);
       return;
       }

  b = (BYTE)((d >> 8*2) & 0x00ffl);
  fwrite( &b, 1,1,f);
  b = (BYTE)((d >> 8*3) & 0x00ffl) | 0x80;
  fwrite( &b, 1,1,f);
  b = (BYTE)((d >> 8*0) & 0x00ffl);
  fwrite( &b, 1,1,f);
  b = (BYTE)((d >> 8*1) & 0x00ffl);
  fwrite( &b, 1,1,f);
  return;
}


/** Check for existency of a given file with PS contents. */
static bool CheckPreviousPS(const char *NewFilename)
{
FILE *PostScript;
char c1, c2;

  if((PostScript=fopen(NewFilename,"r"))!=NULL)
    {
    string s;
    c1 = getc(PostScript);
    c2 = getc(PostScript);
    if(c1=='%' && c2=='!')
      {
      fGets2(PostScript,s);
      fGets2(PostScript,s);
      fGets2(PostScript,s);
      if(!(string("wp2latex") IN s))
	{
	fclose(PostScript);
	return true;		//file already exist, do not overwrite
	}
      }
    fclose(PostScript);
    }
  return false;
}


/** This procedure creates empty PostScript image with warning about inability to convert given image. */
static void MakeDummyPS(const char *Filename, Image &Img)
{
VectorImage *Vec = new VectorImage();
  
  Vec->PSS.FontSizeW = Vec->PSS.FontSize = 140;
  Vec->PSS.LineColor.Red = Vec->PSS.LineColor.Green = Vec->PSS.LineColor.Blue = 255;
  Vec->PSS.TextColor.Red = Vec->PSS.TextColor.Green = Vec->PSS.TextColor.Blue = 255;

  VectorRectangle *Vr = new VectorRectangle(0,2790, 0,5370);
  Vr->LineColor.Red = Vr->LineColor.Green = Vr->LineColor.Blue = 255;
  Vr->BrushStyle = 0;
  Vr->LineStyle = 1;
  Vec->AddObject(Vr);

  TextContainer *Tc = new TextContainer();
  Tc->AddText("Please convert an image", Vec->PSS);
  Tc->PosX=1095; Tc->PosY=2150;
  Vec->AddObject(Tc);
  Tc = new TextContainer();
  Tc->AddText("to the PostScript", Vec->PSS);
  Tc->PosX=1615; Tc->PosY=955;
  Vec->AddObject(Tc);
  Tc = new TextContainer();
  Tc->AddText("manually!", Vec->PSS);
  Tc->PosX=2020; Tc->PosY=415;
  Vec->AddObject(Tc);

  Tc = new TextContainer();
  int Expand = 0;
  int FnameLen = 0;
  if(Filename!=NULL)
    {
    FnameLen = strlen(Filename);
    for(int i=0; i<FnameLen; i++)
      {
      char ch = Filename[i];
      if(ch=='\\')
        Expand++;
      }
    }

  Tc->PosX = 5370/2 - FnameLen*Vec->PSS.FontSize; 
  if(Tc->PosX < 0) Tc->PosX = 0;
  Tc->PosY = 1525;

  if(Expand<=0)
    {
    Tc->AddText(Filename, Vec->PSS);
    }
  else			// Expand>0
    {
    char *Text2 = (char*)malloc(FnameLen+Expand+1);
    int i = 0;
    Expand = 0;
    while(i<FnameLen)
      {
        char ch = Filename[i];
        if(ch=='\\')
          {
          Text2[i+Expand] = '\\';
          Expand++;
          }
        Text2[i+Expand] = ch;
        i++;
      }
    Text2[FnameLen+Expand] = 0;
    Tc->AddText(Text2, Vec->PSS);
    free(Text2);
    }
   
  Vec->AddObject(Tc);

  Img.AttachVecImg(Vec);
  Img.x = 0;	Img.dx = PS2WPG(Vr->RightRect);
  Img.y = 0;	Img.dy = PS2WPG(Vr->TopRect);
}


static void NoImgMemory(TconvertedPass1 *cq,int Width,int Height)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#NoImgMemory() ");fflush(cq->log);
#endif
if (cq->err != NULL)
   {
   cq->perc.Hide();
   if(Width==0 || Height==0)
       fprintf(cq->err, _("\nWarning: Zero size raster (%dx%d)?"),Width,Height);
   else
       fprintf(cq->err, _("\nError: Not enough memory to store image raster (%dx%d)!"),Width,Height);
   }
}


/*static int SetCategory(FILE *strip,const set & FixChars)
{
unsigned char ch;
int i=0;

if(strip==NULL) return(0);
for(ch=1;ch<255;ch++)
	{
	if(FixChars[ch])
		{
		i++;
		fprintf(strip,"\\catcode`\\%c=12\n",ch);
		}
	}
return(i);
}*/


void AddCharacterToContainer(TextContainer *pTextCont, WORD WchI, CpTranslator *PsNativeCP, CpTranslator *PsNativeSym, 
		TconvertedPass1 *cq, PS_State &PSS)
{
  if(WchI==0) return;

  if(PsNativeCP != NULL)
    {
    char ch = (*PsNativeCP)[WchI];
    if(ch == '\\')
      {
      pTextCont->AddText("\\\\",PSS);
      return;
      }
    if(ch != 0)
      {
      pTextCont->AddText(ch,PSS);
      return;
      }
    }
  if(PsNativeSym != NULL)
    {
    char ch = (*PsNativeSym)[WchI];
    if(ch != 0)
      {
      pTextCont->AddText(ch,"Symbol",PSS);
      return;
      }
    }

  const char *str = Ext_chr_str(WchI, cq);
  if(str==NULL)       
    {
    pTextCont->AddText("_",PSS);
    return;
    }

  if(!strcmp(str,"$\\sum$"))	// Replace Summation with sigma.
  {
    pTextCont->AddText("S","Symbol",PSS);
    return;
  }

  {
    temp_string contents(str);

    contents = replacesubstring(contents,"\\'{\\i}","\\'{\\365}");
    contents = replacesubstring(contents,"$-$","-");
    contents = replacesubstring(contents,"$>$",">");
    contents = replacesubstring(contents,"$<$","<");
    contents = replacesubstring(contents,"!`","\\241");
    contents = replacesubstring(contents,"{c}\\llap{/}","\\242");
    contents = replacesubstring(contents,"\\cent ","\\242");
    contents = replacesubstring(contents,"\\textcentoldstyle{}","\\242");
    contents = replacesubstring(contents,"\\pounds{}","\\243");
    contents = replacesubstring(contents,"\\textyen{}","\\245");
    contents = replacesubstring(contents,"\\degrees ","\\312");
    contents = replacesubstring(contents,"\\textdegree{}","\\312");
    contents = replacesubstring(contents,"$\\tt\\backslash$","\\");

    pTextCont->AddText(contents,PSS);
  }
}


/** Convert a block of WP5.x text into PostScript. */
static void Textl2_2PS(TconvertedPass1 *cq, VectorList &VectList, long ldblk, PS_State &PSS, WPG_records *WPG)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#Textl2_2PS() ");fflush(cq->log);
#endif
BYTE TextColorIdx;
const RGB_Record BkTextColor = PSS.TextColor;
const float BkFontSize = PSS.FontSize;
WORD FontSize = 1100;		// Default font size
WORD MaxFontSize = 0;
short LineCount = 1;
const unsigned short CodePageBk = OutCodePage;
long ActualPos;
TconvertedPass1 *cq5;
CpTranslator *PsNativeCP = GetTranslator("internalTOcp1276");
CpTranslator *PsNativeSym = GetTranslator("internalTOsymbol");  

  if(ldblk<=0) return;
  cq5 = GetConverter("WP5.x");
  if(cq5==NULL) 
    {
    if(cq->err!=NULL && Verbosing>=0)
      {
      cq->perc.Hide();
      fprintf(cq->err, _("\nError: WP5.x converter is not available in this build!"));
      }
    return;
    }

  TextContainer *pTextCont = new TextContainer;  
  //pTextCont->FontOrientation = PSS.FontOrientation10 / 10.0f;

  cq5->InitMe(cq->wpd,cq->table,cq->strip,cq->log,cq->err);
  cq5->recursion++;
  if(cq5->ConvertCpg==NULL)
    cq5->ConvertCpg = GetTranslator("wp5TOinternal");

  cq5->ActualPos=cq->ActualPos+21;
  ldblk+=cq5->ActualPos;
  fseek(cq5->wpd,cq5->ActualPos,SEEK_SET);
  cq5->flag = Nothing;
  OutCodePage = 0;
  while(cq5->ActualPos<ldblk)
    {
    *cq5->ObjType=0;
    cq5->by = fgetc(cq5->wpd);
    if(feof(cq5->wpd)) break;

    if(cq5->by==0xC0)
      {
      ActualPos = ftell(cq5->wpd);
      WORD WChar;
      Rd_word(cq->wpd, &WChar);
      if(cq5->log != NULL) 
	fprintf(cq5->log, "[%d,%d]", WChar/255, WChar&0xFF);
      if(cq5->ConvertCpg)
        AddCharacterToContainer(pTextCont, (*cq5->ConvertCpg)[WChar], PsNativeCP, PsNativeSym, cq, PSS);
      fseek(cq5->wpd,ActualPos,SEEK_SET);
      }

    if(cq5->by == 0x0A ||	// [HRt]
       cq5->by == 0x0D ||	// [SRt]
       cq5->by == 0x8C)		// [HRT]
      {
      pTextCont->AddText('\n',PSS);
      LineCount++;
      }

    if(cq5->by>=32 && cq5->by<=0x80)
      {
      AddCharacterToContainer(pTextCont, (*cq5->ConvertCpg)[cq5->by], PsNativeCP, PsNativeSym, cq, PSS);
      }

    if(cq5->by == 0xD1)
      {
      ActualPos=ftell(cq5->wpd);
      fread(&cq5->subby, 1, 1, cq5->wpd);
      if(cq5->subby==1)
	{
	WORD FontSize2;
	fseek(cq5->wpd,28+2,SEEK_CUR);
        Rd_word(cq5->wpd, &FontSize2);
        sprintf(cq5->ObjType,"Font Size %dWPu",FontSize2);

        if(FontSize2!=FontSize)
          {                  
          FontSize = FontSize2;
          PSS.FontSize = WPGu2mm(FontSize);
          if(MaxFontSize <= PSS.FontSize) MaxFontSize=PSS.FontSize;
	  }        
	}
      if(cq5->subby==2)
	{
	fseek(cq5->wpd,1+2,SEEK_CUR);
	TextColorIdx = fgetc(cq5->wpd);

        RGB_Record NewColor = WPG_Palette[TextColorIdx];
        if(memcmp(&NewColor,&PSS.TextColor,sizeof(PSS.TextColor)))
          {                
	  PSS.TextColor = NewColor;
          }
	}
      fseek(cq5->wpd,ActualPos,SEEK_SET);
      }

    cq5->Dispatch(DISP_PROCESSKEY);
    }

 if(cq5!=cq)
	delete cq5;
 OutCodePage = CodePageBk;

 if(!pTextCont->isEmpty())
 {
   if(MaxFontSize <= 0) MaxFontSize=PSS.FontSize;

   float y = WPGu2PSu(WPG->TextL2.LowLeftY);
   float dy = WPGu2PSu(WPG->TextL2.UpRightY-WPG->TextL2.LowLeftY);
   if(LineCount >1)
     {
     dy /= LineCount;
     y += (LineCount-1) * dy;
     }

   pTextCont->PosX = WPGu2PSu(WPG->TextL2.LowLeftX);
   pTextCont->PosY = y;
   pTextCont->PosY += dy/2;
   pTextCont->PosY -= MaxFontSize*2.66/2;
   pTextCont->PosY += 0.15*MaxFontSize*2.66/2;		// Shift text slightly up
   pTextCont->FontOrientation = WPG->TextL2.RotAngle;
   pTextCont->RotCenterX = WPGu2PSu(WPG->TextL2.UpRightX - WPG->TextL2.LowLeftX) / 2;
   pTextCont->RotCenterY = dy / 2;  
   VectList.AddObject(pTextCont);
 }
 else
   delete pTextCont;

 PSS.TextColor = BkTextColor;
 PSS.FontSize = BkFontSize;
}


extern unsigned char CharsWP6_1_32[0x21];

/** Convert a block of WP6.x text into PostScript. */
static TextContainer *TextWPG2_2PS(TconvertedPass1 *cq, long ldblk, string & PSData, PS_State &PSS,
		float x, float y, float dx, float dy, float Angle)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#TextWPG2_2PS(%ld) ",ldblk);fflush(cq->log);
#endif
TextContainer *pTxC;
WORD FontSize = 1100;
short LineCount =1;
const unsigned short CodePageBk = OutCodePage;
RGB_Record BkTextColor = PSS.TextColor;
TconvertedPass1 *cq6;				// Converter for embedded text fragment
long ActualPos;
CpTranslator *PsNativeCP = GetTranslator("internalTOcp1276");
CpTranslator *PsNativeSym = GetTranslator("internalTOsymbol");  

  cq6 = GetConverter("WP6.x");
  if(cq6==NULL) return NULL;

  cq->ActualPos = ftell(cq->wpd);
  ldblk+=cq->ActualPos;			//end position
  // Rd_word(cq->wpd, &StringSize); ?????

  cq6->InitMe(cq->wpd,cq->table,cq->strip,cq->log,cq->err);
  if(cq6->ConvertCpg==NULL)
    cq6->ConvertCpg = GetTranslator("wp6TOinternal");
  cq6->recursion++;
  cq6->ActualPos=cq->ActualPos;
  cq6->flag = Nothing;

  pTxC = new TextContainer();  

  OutCodePage = 0;
  while(cq6->ActualPos<ldblk)
    {
    *cq6->ObjType = 0;
    cq6->by = fgetc(cq6->wpd);

    if(cq6->by==0xD4 || cq6->by==0xE1)
      {
      ActualPos = ftell(cq6->wpd);
      cq6->subby = fgetc(cq6->wpd);
      if(cq6->by==0xD4)
	{
	if(cq6->subby==0x1B)
	  {
	  fseek(cq6->wpd,6+2,SEEK_CUR); //<flags><PIDsNo>[descriptor][NDelSize]
          Rd_word(cq6->wpd, &FontSize);
	  PSS.FontSize = FontSize / (2.66*50);            	  
	  }
	}

    if(cq6->by==0xE1)
      {
      if(cq6->subby==0xC)
	  {
	  RGB_Record NewColor;

	  fseek(cq6->wpd,3+2,SEEK_CUR);
	  NewColor.Red = fgetc(cq6->wpd);
	  NewColor.Green = fgetc(cq6->wpd);
	  NewColor.Blue = fgetc(cq6->wpd);

          PSS.TextColor = NewColor;
          
		//transparency is ignored
	  sprintf(cq6->ObjType,"Pen Fore Color %d,%d,%d",PSS.TextColor.Red,PSS.TextColor.Green,PSS.TextColor.Blue);
	  }
	}
      fseek(cq6->wpd,ActualPos,SEEK_SET);
    }

    if(cq6->by == 0xF0)			// Extended character
      {
      ActualPos = ftell(cq6->wpd);
      WORD WChar;      
      Rd_word(cq->wpd, &WChar);
      if(cq6->log != NULL) 
	  fprintf(cq6->log, "[%d,%d]", WChar/255, WChar&0xFF);
      AddCharacterToContainer(pTxC, (*cq6->ConvertCpg)[WChar], PsNativeCP, PsNativeSym, cq6, PSS);
      fseek(cq6->wpd,ActualPos,SEEK_SET);
      }

    if(cq6->by>0x20 && cq6->by<=0x7F)	// Normal_char
      {
      AddCharacterToContainer(pTxC, cq6->by, PsNativeCP, PsNativeSym, cq6, PSS);      
      }

    if(cq6->by>=0x01 && cq6->by<=0x20)	// Default extended international characters (from 0)
      {
      const WORD WChar = 0x100 | CharsWP6_1_32[cq6->by];
      if(cq6->log != NULL) 
	  fprintf(cq6->log, "[%Xh:%d,%d]", (int)cq6->by, WChar/255, WChar&0xFF);
      AddCharacterToContainer(pTxC, (*cq6->ConvertCpg)[WChar], PsNativeCP, PsNativeSym, cq6, PSS);
      }

    if(cq6->by==0x80 ||			// Space
       cq6->by==0x81)        		// Hard space       
      {
      pTxC->AddText(" ",PSS);
      }

    if(cq6->by==0xCC ||			// HRt
       cq6->by==0xC7 ||
       cq6->by==0xD0)			// HRt/Hpg
      {
      LineCount++;
      pTxC->AddText("\n",PSS);
      }

    cq6->Dispatch(DISP_PROCESSKEY);
    }
  if(cq6!=cq)
	delete cq6;

  OutCodePage = CodePageBk; 
  PSS.TextColor = BkTextColor;

  if(!pTxC->isEmpty())
  {
    if(LineCount >1)
    {
      dy /= LineCount;
      y += (LineCount-1) * dy;
    }
    pTxC->PosX = x;
    pTxC->PosY = y;    
    pTxC->PosY += dy/2;
    pTxC->PosY -= PSS.FontSize*2.66/2;
    pTxC->PosY += 0.15*PSS.FontSize*2.66/2;		// Shift text slightly up
    pTxC->FontOrientation = Angle;
    pTxC->RotCenterX = dx / 2;
    pTxC->RotCenterY = dy / 2;  
    //VectList.AddObject(pTextCont);
  } 
 
  return pTxC;
}


/** Load array of points word packed. */
float *LoadPoints(TconvertedPass1 *cq, int n, FloatBBox &bbx, AbstractTransformXY *TRx, bool Precision)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#LoadPoints(%d) ",n);fflush(cq->log);
#endif
float *Points, *p;

  if(n<=0) return(NULL);
  p = Points = (float*)calloc(2*n,sizeof(*Points));
  if(Points==NULL) return(NULL);

  float x, y;  
  while(n-->0)
    {
      if(!Precision)
        {
        SWORD w;
	Rd_word(cq->wpd, (WORD *)&w);
	x = w;
	Rd_word(cq->wpd,(WORD *)&w);
	y = w;
        }
      else
        {
        SDWORD d;
	Rd_dword(cq->wpd, (DWORD *)&d);
	x = d / 65536.0;
	Rd_dword(cq->wpd,(DWORD *)&d);
	y = d / 65536.0;
        }

      if(TRx) TRx->ApplyTransform(x,y);

      if(x<bbx.MinX) bbx.MinX=x;
      if(x>bbx.MaxX) bbx.MaxX=x;
      if(y<bbx.MinY) bbx.MinY=y;
      if(y>bbx.MaxY) bbx.MaxY=y;

      *p++ = x;
      *p++ = y;
      }
#ifdef DEBUG
  fprintf(cq->log,"\n#~LoadPoints(%d) ",n);fflush(cq->log);
#endif
  return(Points);
}


/** Check capacity of object for a given amount of polygons. */
static void FixPolySize(TconvertedPass1 *cq, WORD *P_size, DWORD ObjSize)
{
  if(ObjSize < 2+2*(DWORD)*P_size)
    {
    if (cq->err != NULL)
       {
       cq->perc.Hide();
       fprintf(cq->err, _("\nError: The size of polygon %ld is not enough to fit %d elements!"), (long)ObjSize, *P_size);
       }
    if(ObjSize<=2) *P_size=0;
              else *P_size=(ObjSize-2)/2;
    }
}


/** This function generates unique name for the image - if the original one is empty.
 * Please use extension maximally 3 characters long, othervise crash occurs. */
char *GetSomeImgName(const char *Ext)
{
static char tmp_img[11] = "00_img.wpg";
char *ptrch;

  switch(tmp_img[0])	/*Actualise a name*/
    {
    case '9':tmp_img[0] = 'a';
	     break;
    case 'z':tmp_img[0] = '0';
	     switch(tmp_img[1])
	       {
	       case '9':tmp_img[1] = 'a'; break;
	       case 'z':tmp_img[1] = '0';
			if(tmp_img[2]=='_') tmp_img[2]='0';
				       else tmp_img[2]++;
			break;
	       default: tmp_img[1]++; break;
	       }
	     break;
    default: tmp_img[0]++; break;
    }

  ptrch=tmp_img+6;	/*prepare extension*/
  *ptrch=0;
  if(Ext)
    do
     {
     *ptrch++=*Ext;
     } while(*Ext++!=0);
  return(tmp_img);
}


static vecTransform *BuildTransform(const WPG2_Transform &TRx, SDWORD CenterX, SDWORD CenterY)
{
 vecTransform *Tx = new vecTransform;
 Tx->CenterX = WPGu2PSu(CenterX);
 Tx->CenterY = WPGu2PSu(CenterY);
 Tx->RotAngle = TRx.RotAngle;
 if(TRx.Flags & WPG2_Transform::WPG2_TRN)
   {
   Tx->TranslateX = WPGu2PSu(TRx.TranslateX);
   Tx->TranslateY = WPGu2PSu(TRx.TranslateY);
   }
 if(TRx.Flags & WPG2_Transform::WPG2_SCL)
   {
   Tx->ScaleX = TRx.ScaleX * cos(M_PI*Tx->RotAngle/180.0);
   Tx->ScaleY = TRx.ScaleY * cos(M_PI*Tx->RotAngle/180.0);
   }
 return Tx;
}


static void BuildTM(const WPG2_Transform &TRx, float_matrix &TM, bool rotations=false)
{
 TM.member(0,0)=TRx.ScaleX;    TM.member(1,0)=TRx.SkewY;     TM.member(2,0)=TRx.TamperX;
 TM.member(0,1)=TRx.SkewX;     TM.member(1,1)=TRx.ScaleY;    TM.member(2,1)=TRx.TamperY;
 TM.member(0,2)=TRx.TranslateX;TM.member(1,2)=TRx.TranslateY;TM.member(2,2)=1;

 if(rotations)
 {
   float val = cos(M_PI*TRx.RotAngle/180.0);
   TM.member(0,0) *= val;
   TM.member(1,1) *= val;
   val = sin(180*TRx.RotAngle/M_PI);   
   TM.member(1,0) *= val;
   TM.member(0,1) *= -val;
 }
}


/** This functor class is used for resizing image data. */
class vecResizeCTM: public AbstractTransformXY, public WPG2_Transform
{
public:
  vecResizeCTM(void) {}

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


void vecResizeCTM::ApplyTransform(float &x, float &y) const
{
const float NewX = 
     ScaleX*x + SkewX*y  + TranslateX;
 y = SkewY*x  + ScaleY*y + TranslateY;
 x = NewX;
}


class TImgConvStatus
{
public:
  float x, y, dx, dy, RotAngle;
  WORD BrushCount;
  WORD MaxBrushes;
  Raster2DAbstract **BrushList;

  TImgConvStatus(void)	{x=y=dx=dy=RotAngle = MaxBrushes=BrushCount=0; BrushList=NULL;}
  ~TImgConvStatus() {ClearBrushes();}

  void ClearBrushes(void);

};

void TImgConvStatus::ClearBrushes(void)
{
 if(BrushList==NULL) return;
 
 for(int i=0; i<BrushCount; i++)
   {
   if(BrushList[i]!=NULL)
     {
     if(BrushList[i]->UsageCount--<=1) delete BrushList[i];
     BrushList[i] = NULL;
     }
   }
 free(BrushList);
 BrushList = NULL;
 MaxBrushes = BrushCount = 0;
}


/** Processing one token inside separate procedure allows to recurse tokens. 
 * @param[in,out]	bbx	Bounding box [WPU]. */
static void ProcessWPG2Token(TconvertedPass1 *cq, WPG2Record & Rec2,
      WPG2Start & StartWPG, Image & Img, APalette **Palette,
      string & PSData, PS_State & PSS, EmbeddedImg **EmImg,
      DWORD & NewObject, DWORD & ResourceEnd, Image **CurrImg,
      TImgConvStatus & ImgSts, FloatBBox & bbx, VectorList &VectList)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ProcessWPG2Token() ");fflush(cq->log);
#endif
WPG_records WPG;
WORD i;
Raster2DAbstract *Raster = NULL;
long ldblk;
vecResizeCTM TRx;

  switch(Rec2.Type)
    {
    case 0x1:strcpy(cq->ObjType,"Start WPG2");
	     LoadWPG2Start(cq->wpd,StartWPG);
	     if(StartWPG.PosSizePrecision!=0)
		{
		cq->perc.Hide();
		fprintf(cq->err, _("\nError: Unsupported precision %d, please send me this image for analysis."), (int)StartWPG.PosSizePrecision);
		}
	      break;
    case 0x2:strcpy(cq->ObjType,"End WPG");
		 NewObject=ResourceEnd;    //skip everything after this
		 break;
    case 0x3:strcpy(cq->ObjType,"!Form Settings"); break;
    case 0x4:strcpy(cq->ObjType,"!Ruller Settings"); break;
    case 0x5:strcpy(cq->ObjType,"!Grid Settings"); break;
    case 0x6:strcpy(cq->ObjType,"!Layer"); break;
    case 0x7:strcpy(cq->ObjType,"!Object Link"); break;
    case 0x8:strcpy(cq->ObjType,"!Pen style definition"); break;
    case 0x9: {
              strcpy(cq->ObjType,"Pattern Definition");
              ImgSts.ClearBrushes();
              WORD PatternIndex;
              RdWORD_LoEnd(&PatternIndex,cq->wpd);
              WORD NumberOfResolutions;
              RdWORD_LoEnd(&NumberOfResolutions,cq->wpd);
              ImgSts.MaxBrushes = NumberOfResolutions + 1;              
	      if(ImgSts.MaxBrushes >= 1)
                {
                ImgSts.BrushList = (Raster2DAbstract**)calloc(ImgSts.MaxBrushes, sizeof(Raster2DAbstract **));
                if(ImgSts.BrushList==NULL)
                    ImgSts.MaxBrushes = 0;
                }
              break;
              }
    case 0xA:strcpy(cq->ObjType,"!Comment"); break;
    case 0x0B:strcpy(cq->ObjType,"!Color Transfer"); break;
    case 0x0C:strcpy(cq->ObjType,"Color Palette");
	      LoadWPGColormap(cq->wpd,WPG.ColorMapRec);		// color map header
	      if(Palette==NULL) break;
	      if(*Palette!=NULL && (*Palette)->UsageCount==0) delete *Palette;
	      *Palette = BuildPalette(WPG.ColorMapRec.NumOfEntries+WPG.ColorMapRec.StartIndex,8);
	      if(*Palette!=NULL)
		{
		for(i=WPG.ColorMapRec.StartIndex; i<WPG.ColorMapRec.NumOfEntries; i++)
		    {
		    (*Palette)->R(i,fgetc(cq->wpd));
		    (*Palette)->G(i,fgetc(cq->wpd));
		    (*Palette)->B(i,fgetc(cq->wpd));
		    fgetc(cq->wpd);//Opacity?
		    }
		}
		break;
    case 0x0D:strcpy(cq->ObjType,"DP Color Palette"); 
              LoadWPGColormap(cq->wpd,WPG.ColorMapRec);		// color map header
	      if(Palette==NULL) break;
	      if(*Palette!=NULL && (*Palette)->UsageCount==0) delete *Palette;
	      *Palette = BuildPalette(WPG.ColorMapRec.NumOfEntries+WPG.ColorMapRec.StartIndex,16);
	      if(*Palette!=NULL)
		{
		for(i=WPG.ColorMapRec.StartIndex; i<WPG.ColorMapRec.NumOfEntries; i++)
		    {
                    WORD num;
                    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->R(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->G(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->B(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	//Opacity?
		    }
		}

              break;
    case 0x0E:strcpy(cq->ObjType,"Bitmap Data");
	      LoadWPG2BitmapData(cq->wpd,WPG._2BitmapData);
	      if(WPG._2BitmapData.Compression>1)
		 {
		 cq->perc.Hide();
		 fprintf(cq->err,_("Error: Unsupported WPG2 compression %d, please report."),WPG._2BitmapData.Compression);
		 return;
		 }
	      switch(WPG._2BitmapData.Depth)
		{
		case 1:i=1;break;
		case 2:i=2;break;
		case 3:i=4;break;
		case 4:i=8;break;
		case 8:i=24;break;
		default: return;		// Ignore raster with unknown depth.
		}
	      Raster = CreateRaster2D(WPG._2BitmapData.Width,WPG._2BitmapData.Height,i);
	      if(Raster==NULL)
		  {NoImgMemory(cq,WPG._2BitmapData.Width,WPG._2BitmapData.Height);return;}

	      if(WPG._2BitmapData.Compression==1)
		{
		if(UnpackWPG2Raster(Raster,cq->wpd)<0)
		  {
		  Raster->Erase();
		  if (cq->err != NULL)
		    {
		    cq->perc.Hide();
		    fprintf(cq->err, _("\nError: cannot decompress current WPG raster, skipping!"));
		    }
		  }
	       }
	       if(WPG._2BitmapData.Compression==0)
		 {
		 ldblk = (Raster->GetPlanes()*Raster->Size1D+7)/8;
		 for(i=0;i<Raster->Size2D;i++)
		     {
		     if(fread(Raster->GetRow(i),ldblk,1,cq->wpd)!=1) break;
	             // AlineProc(i,p);
		     }
		 }

               if(ImgSts.BrushCount<ImgSts.MaxBrushes && ImgSts.BrushList!=NULL)
                 {		// Read brush pattern and not a regular image.
                 Raster->UsageCount++;
                 ImgSts.BrushList[ImgSts.BrushCount] = Raster;
                 ImgSts.BrushCount++;
                 Raster = NULL;                 
                 return;
                 }

	       if(Img.Raster!=NULL)
	         {
	         (*CurrImg)->Next = new Image;
	         (*CurrImg) = (*CurrImg)->Next;
	         }
		 
               if(ImgSts.dx>=0)
                   {(*CurrImg)->x = ImgSts.x; (*CurrImg)->dx = ImgSts.dx;}
               else
               {
                 (*CurrImg)->x = ImgSts.x; (*CurrImg)->dx = -ImgSts.dx;
                 if(Raster!=NULL) Flip1D(Raster);
               }
               if(ImgSts.dy>=0)
                   {(*CurrImg)->y = ImgSts.y; (*CurrImg)->dy = ImgSts.dy;}
               else
               {
                 (*CurrImg)->y = ImgSts.y; (*CurrImg)->dy = -ImgSts.dy;
                 if(Raster!=NULL) Flip2D(Raster);
               }

               (*CurrImg)->RotAngle = ImgSts.RotAngle;
	       ImgSts.x = ImgSts.y = ImgSts.dx = ImgSts.dy = 0;
	       if(Raster!=NULL)
	       {
		  (*CurrImg)->Raster=Raster;(*CurrImg)->Raster->UsageCount++;Raster=NULL;
	       }
	       AssignPalette(*CurrImg,*Palette);
	       break;
    case 0x0F:{//CrackObject(cq,NewObject);
			// Debug purpose rectangle
              /* VectorRectangle *Vr = new VectorRectangle(WPGu2PSu(ImgSts.y), WPGu2PSu(ImgSts.y+ImgSts.dy), WPGu2PSu(ImgSts.x), WPGu2PSu(ImgSts.x+ImgSts.dx));
	      PSS.FillPattern = 0;
	      Vr->AttribFromPSS(PSS);
	      VectList.AddObject(Vr); */
              UpdateBBox(bbx, ImgSts.RotAngle, ImgSts.x,ImgSts.y,ImgSts.dx,ImgSts.dy);	// Calculate BBX in [WPU].
	      TextContainer *TxC = TextWPG2_2PS(cq,Rec2.RecordLength,PSData,PSS,WPGu2PSu(ImgSts.x),WPGu2PSu(ImgSts.y),WPGu2PSu(ImgSts.dx),WPGu2PSu(ImgSts.dy),ImgSts.RotAngle);
	      if(TxC!=NULL) VectList.AddObject(TxC);
	      strcpy(cq->ObjType,"Text Data");
	      break;
	      }
    case 0x10:strcpy(cq->ObjType,"!Chart Style");break;
    case 0x11:strcpy(cq->ObjType,"Chart Data");break;
    case 0x12:{strcpy(cq->ObjType,"Object Image");	/* PostScript or another format inside WPG2*/
		  //CrackObject(cq,NewObject);//!!!!
		  WORD AccessoryDataLen;
		  Rd_word(cq->wpd,&AccessoryDataLen);
		  if(Rec2.RecordLength>0x12)
		    if(EmImg!=NULL)
		      {
		      EmbeddedImg *Ei2 = (EmbeddedImg *)malloc(sizeof(EmbeddedImg));
		      Ei2->Offset = cq->ActualPos+AccessoryDataLen+sizeof(AccessoryDataLen);   /*skip accessory data in the wpg2*/
		      Ei2->Length = Rec2.RecordLength-AccessoryDataLen-sizeof(AccessoryDataLen);
		      Ei2->Extension = Ei2->ImgName = NULL;
		      Ei2->Next = *EmImg;
		      *EmImg = Ei2;
		      }
		  break;
		  }
    case 0x15:{strcpy(cq->ObjType,"Polyline");
		  float *Points;

		  TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);

		  Rd_word(cq->wpd,&WPG.Curve.Count);
		  FixPolySize(cq, &WPG.Curve.Count, (StartWPG.PosSizePrecision==0)?Rec2.RecordLength:(Rec2.RecordLength/2)); //insufficient fix -sizeof(CTM)!!!
		  Points = LoadPoints(cq, WPG.Curve.Count, bbx, &TRx, StartWPG.PosSizePrecision>0);
		  if(Points==NULL) break;

		  if(TRx.Flags & WPG2_Transform::WPG2_FILL)	//check Fill flag
                      {
                      VectorPolygon *pVecPoly = new VectorPolygon(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecPoly->AttribFromPSS(PSS);  
                      pVecPoly->Close = true;
                      pVecPoly->Outline = TRx.Flags&WPG2_Transform::WPG2_FRM;
                      if((TRx.Flags & WPG2_Transform::WPG2_FRM) == 0) pVecPoly->LineStyle=0;	// Disable object fill.
                      else
                          {if(pVecPoly->LineStyle==0) pVecPoly->LineStyle=1;}                      
                      pVecPoly->Transform(vecResizeXY(WPGu2PSu(1)));
                      VectList.AddObject(pVecPoly);
                      }
		  else
                      {
                      VectorLine *pVecLine = new VectorLine(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecLine->AttribFromPSS(PSS);  
                      pVecLine->Close = TRx.Flags&WPG2_Transform::WPG2_CLS;                      
                      pVecLine->Transform(vecResizeXY(WPGu2PSu(1)));
                      VectList.AddObject(pVecLine);
                      }
                   		  
		  break;
		  }
    case 0x16:strcpy(cq->ObjType,"!Polyspline");break;
    case 0x17:{strcpy(cq->ObjType,"Polycurve");
		  float *Points;

		  TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);
			//!!RotAngle Ignored!!!
		  Rd_word(cq->wpd,&WPG.Curve.Count);
		  WPG.Curve.Count *= 3;
		  FixPolySize(cq, &WPG.Curve.Count, (StartWPG.PosSizePrecision==0)?Rec2.RecordLength:(Rec2.RecordLength/2)); //insufficient fix -sizeof(CTM)!!!
		  Points = LoadPoints(cq,WPG.Curve.Count,bbx,NULL,StartWPG.PosSizePrecision>0);		 
		  if(Points==NULL) break;
                  VectorCurve *pVecCurve = new VectorCurve(Points, WPG.Curve.Count);
                  pVecCurve->AttribFromPSS(PSS);
                  pVecCurve->Transform(vecResizeXY(WPGu2PSu(1)));		  
                  if(TRx.Flags&WPG2_Transform::WPG2_FILL) pVecCurve->Filled=true;	// check Fill
                  if(TRx.Flags&WPG2_Transform::WPG2_FRM) pVecCurve->LineStyle=0;	// Frame flags
		  VectList.AddObject(pVecCurve);
		  break;
		  }
    case 0x18:{strcpy(cq->ObjType,"Rectangle");
		  TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);
		  ImgSts.RotAngle = TRx.RotAngle;

		  LoadWPG2Rectangle(cq->wpd,WPG._2Rect);
		  UpdateBBox(bbx, 0, WPG._2Rect.X_ur,WPG._2Rect.Y_ll,WPG._2Rect.X_ll-WPG._2Rect.X_ur,WPG._2Rect.Y_ur-WPG._2Rect.Y_ll);

                  VectorRectangleArc *pRectArc = new VectorRectangleArc(
                      WPGu2PSu(WPG._2Rect.X_ll), WPGu2PSu(WPG._2Rect.Y_ll), WPGu2PSu(WPG._2Rect.X_ur), WPGu2PSu(WPG._2Rect.Y_ur),
                      WPGu2PSu(WPG._2Rect.H_radius), WPGu2PSu(WPG._2Rect.V_radius));
                  pRectArc->AttribFromPSS(PSS);    
                  VectList.AddObject(pRectArc);
		  break;
		  }
    case 0x19:{strcpy(cq->ObjType,"Arc");
                  //CrackObject(cq,NewObject);
		  TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);
		  LoadWPG2Arc(cq->wpd,WPG._2Arc);

                  float Sx = WPG._2Arc.Sx;
                  float Sy = WPG._2Arc.Sy;
                  TRx.ApplyTransform(Sx,Sy);
                  float LeftX = WPG._2Arc.Sx - WPG._2Arc.rx;
                  float LeftY = WPG._2Arc.Sy;
                  TRx.ApplyTransform(LeftX,LeftY);
                  float TopX = WPG._2Arc.Sx;
                  float TopY = WPG._2Arc.Sy + WPG._2Arc.ry;
                  TRx.ApplyTransform(TopX,TopY);

		  float rx = sqrt((Sx-LeftX)*(Sx-LeftX) + (Sy-LeftY)*(Sy-LeftY));
                  float ry = sqrt((Sx-TopX)*(Sx-TopX) + (Sy-TopY)*(Sy-TopY));
                  VectorEllipse *pVecEllipse = new VectorEllipse(Sy-ry, Sy+ry, Sx-rx, Sx+rx);
                  if(fabs(TRx.RotAngle) > 1e-3)
                    {
                    pVecEllipse->Tx = new vecTransform;
                    pVecEllipse->Tx->RotAngle = TRx.RotAngle;
                    pVecEllipse->Tx->CenterX = Sx;
                    pVecEllipse->Tx->CenterY = Sy;
                    }
                  if((WPG._2Arc.Xi!=WPG._2Arc.Xt || WPG._2Arc.Yi!=WPG._2Arc.Yt) &&
                     WPG._2Arc.rx!=0 && WPG._2Arc.ry!=0)
                    {
                    pVecEllipse->bAngle = 180*atan2(WPG._2Arc.Yi/(float)WPG._2Arc.ry, WPG._2Arc.Xi/(float)WPG._2Arc.rx)/M_PI;
                    pVecEllipse->eAngle = 180*atan2(WPG._2Arc.Yt/(float)WPG._2Arc.ry, WPG._2Arc.Xt/(float)WPG._2Arc.rx)/M_PI;
                    if(pVecEllipse->bAngle<0) pVecEllipse->bAngle+=360;
                    if(pVecEllipse->eAngle<0) pVecEllipse->eAngle+=360;
                    }

                  UpdateBBox(bbx, TRx.RotAngle, Sx-rx, Sy-ry, 2*rx, 2*ry);                  
                  pVecEllipse->Transform(vecResizeXY(WPGu2PSu(1)));

                  pVecEllipse->AttribFromPSS(PSS);
                  if((TRx.Flags & WPG2_Transform::WPG2_FILL) == 0) pVecEllipse->BrushStyle=0;	// Disable object fill.
                  if((TRx.Flags & WPG2_Transform::WPG2_FRM) == 0) pVecEllipse->LineStyle=0;	// Disable object fill.
                  else
                    {if(pVecEllipse->LineStyle==0) pVecEllipse->LineStyle=1;}
                  VectList.AddObject(pVecEllipse);
		  break;
		  }

    case 0x1A:strcpy(cq->ObjType,"!Compound Polygon");break;
    case 0x1B:{strcpy(cq->ObjType,"Bitmap position");
	      //CrackObject(cq,NewObject);//!!!!
	      TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);	      
              //printf("\n Rot angle = %f    ", RotAngle);
              
              float Sx, Sy;
              float RightX, RightY;
              float TopX, TopY;                  
	      if(StartWPG.PosSizePrecision==0)
		 {
		 LoadWPG2BitmapRectangle(cq->wpd,WPG._2BitmapRectangle);
                 Sx = (WPG._2BitmapRectangle.LowLeftX + WPG._2BitmapRectangle.UpRightX) / 2;
                 Sy = (WPG._2BitmapRectangle.LowLeftY + WPG._2BitmapRectangle.UpRightY) / 2;
                 RightX = WPG._2BitmapRectangle.UpRightX;                 
                 TopY = WPG._2BitmapRectangle.UpRightY;                 
		 }
	      if(StartWPG.PosSizePrecision==1)
		 {
		 LoadWPG2DblBitmapRectangle(cq->wpd,WPG._2DblBitmapRectangle);
                 Sx = (WPG._2DblBitmapRectangle.LowLeftX + WPG._2DblBitmapRectangle.UpRightX) / (float)0x20000;
                 Sy = (WPG._2DblBitmapRectangle.LowLeftY + WPG._2DblBitmapRectangle.UpRightY) / (float)0x20000;
                 RightX = WPG._2DblBitmapRectangle.UpRightX / (float)0x10000;               
                 TopY = WPG._2DblBitmapRectangle.UpRightY / (float)0x10000;
		 }
              RightY = Sy;
              TopX = Sx;

              TRx.ApplyTransform(Sx,Sy);				// Image center is rotation invariant.
              TRx.ApplyTransform(RightX,RightY);
              TRx.ApplyTransform(TopX,TopY);

              ImgSts.dx = 2 * sqrt((RightX-Sx)*(RightX-Sx) + (RightY-Sy)*(RightY-Sy));
              ImgSts.dy = 2 * sqrt((TopX-Sx)*(TopX-Sx) + (TopY-Sy)*(TopY-Sy));
              
              if(fabs(RightY-Sy)<1e-5 && fabs(RightX-Sx)<1e-5)
              {
                ImgSts.RotAngle = TRx.RotAngle;
                if(RightX < Sx) ImgSts.dx = -ImgSts.dx;	      
                if(TopY < Sy) ImgSts.dy = -ImgSts.dy;
              }
              else
              {
                ImgSts.RotAngle = 180*atan2(RightY-Sy, RightX-Sx)/M_PI;  // Direct rot angle calculation according to all transforms.
                if((fabs(ImgSts.RotAngle)<90) ^ (TopY>Sy))
		    ImgSts.dy = -ImgSts.dy;
              }

	      ImgSts.x = Sx - fabs(ImgSts.dx)/2.0;
              ImgSts.y = Sy - fabs(ImgSts.dy)/2.0;

	      ImgSts.x /= 47.0;  ImgSts.dx /= 47.0;
	      ImgSts.y /= 47.0;  ImgSts.dy /= 47.0;
	      break;
	      }
    case 0x1C:{
	      strcpy(cq->ObjType,"Text Line");
	      TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);
	      ImgSts.RotAngle = TRx.RotAngle;

	      LoadWPG2TextLine(cq->wpd,WPG._2TxtLine);
	      ImgSts.x = WPG._2TxtLine.Xref;
	      ImgSts.y = WPG._2TxtLine.Yref;
	      UpdateBBox(bbx, 0, ImgSts.x,ImgSts.y,0,0);
	      break;
	      }
    case 0x1D:strcpy(cq->ObjType,"Text Block");
              TRx.LoadWPG2Flags(cq->wpd,StartWPG.PosSizePrecision);
	      ImgSts.RotAngle = TRx.RotAngle;
              if(StartWPG.PosSizePrecision==0)
              {
                LoadWPG2TextBlock(cq->wpd, WPG._2TxtBlock);
                ImgSts.x = WPG._2TxtBlock.LowLeftX;
	        ImgSts.y = WPG._2TxtBlock.LowLeftY;
                ImgSts.dx = WPG._2TxtBlock.UpRightX - WPG._2TxtBlock.LowLeftX;
	        ImgSts.dy = WPG._2TxtBlock.UpRightY - WPG._2TxtBlock.LowLeftY;
              }
              if(StartWPG.PosSizePrecision==1)
              {
		LoadWPG2TextBlockDbl(cq->wpd, WPG._2TxtBlockDbl);
                ImgSts.x = WPG._2TxtBlockDbl.LowLeftX / (float)0x10000;
	        ImgSts.y = WPG._2TxtBlockDbl.LowLeftY / (float)0x10000;
                ImgSts.dx = (WPG._2TxtBlockDbl.UpRightX - WPG._2TxtBlockDbl.LowLeftX) / (float)0x10000;
	        ImgSts.dy = (WPG._2TxtBlockDbl.UpRightY - WPG._2TxtBlockDbl.LowLeftY) / (float)0x10000;
              }
              UpdateBBox(bbx, 0, ImgSts.x, ImgSts.y, ImgSts.dy, ImgSts.dy);		// box specification is stored for further call
              break;
    case 0x1E:strcpy(cq->ObjType,"!Text Path");break;
    case 0x1F:strcpy(cq->ObjType,"!Chart");break;
    case 0x20:strcpy(cq->ObjType,"!Group");break;
    case 0x21:{strcpy(cq->ObjType,"!Object Capsule");break;

			  }
    case 0x22:strcpy(cq->ObjType,"!Font Settings");break;
    case 0x23:strcpy(cq->ObjType,"!Line Cap Definition");break;
    case 0x24:strcpy(cq->ObjType,"!Line Join Definition");break;
    case 0x25:{strcpy(cq->ObjType,"Pen Fore Color");
	      PSS.LineColor.Red=fgetc(cq->wpd);
	      PSS.LineColor.Green=fgetc(cq->wpd);
	      PSS.LineColor.Blue=fgetc(cq->wpd);
	      //PSS.LineColor.Transparency=fgetc(cq->wpd);
	      PSS.dirty |= PSS_LineColor;
	      break;
	      }
    case 0x26:strcpy(cq->ObjType,"!DP Pen Fore Color");break;
    case 0x27:strcpy(cq->ObjType,"!Pen Back Color");break;
    case 0x28:strcpy(cq->ObjType,"!DP Pen Back Color");break;
    case 0x29:{strcpy(cq->ObjType,"Pen Style");
	      BYTE PenStyle;
	      static const BYTE ConvertPen[16]={0,1,2,5,7,10,3,19/*11*/,4,6,8,12,13,16,17/*14*/,18/*15*/};

	      PenStyle=fgetc(cq->wpd);
	      if(PenStyle<16) PenStyle=ConvertPen[PenStyle];
	      if(PSS.LineStyle!=PenStyle)
		 {PSS.LineStyle=PenStyle;PSS.dirty|=PSS_LineStyle;}
	      break;
	      }
    case 0x2A:strcpy(cq->ObjType,"!Pen Pattern");break;
    case 0x2B:{
	      WORD Width, Height;
	      float LineWidth;

	      Rd_word(cq->wpd,(WORD*)&Width);
	      Rd_word(cq->wpd,(WORD*)&Height);
	      LineWidth = WPGu2PSu(Width>Height?Width:Height);
	      if(PSS.LineWidth!=LineWidth)
		{
		PSS.LineWidth = LineWidth;
		PSS.dirty |= PSS_LineWidth;
		}
              sprintf(cq->ObjType,"Pen Size %d", Width>Height?Width:Height);
	      break;
	      }
    case 0x2C:strcpy(cq->ObjType,"!DP Pen Size");break;
    case 0x2D:strcpy(cq->ObjType,"!Line Cap");break;
    case 0x2E:strcpy(cq->ObjType,"!Line Join");break;
    case 0x2F:strcpy(cq->ObjType,"!Brush Gradient");break;
    case 0x30:strcpy(cq->ObjType,"!DP Brush Gradient");break;
    case 0x31:{char GradientType;
	      GradientType = fgetc(cq->wpd);
	      WORD BrushColors = 1;
	      if(GradientType!=0)
	         Rd_word(cq->wpd, &BrushColors);

	      if(BrushColors>=1)
		{
		PSS.FillColor.Red = fgetc(cq->wpd);
		PSS.FillColor.Green = fgetc(cq->wpd);
		PSS.FillColor.Blue = fgetc(cq->wpd);
		PSS.FillTransparency = fgetc(cq->wpd);

		/*if(BrushColors>=2)
		 {
		 PSS.FillColor.Red = fgetc(cq->wpd);
		 PSS.FillColor.Green = fgetc(cq->wpd);
		 PSS.FillColor.Blue = fgetc(cq->wpd);
		 PSS.FillTransparency = fgetc(cq->wpd);
		 }*/

		sprintf(cq->ObjType,"Brush Fore Color n:%d (%2.2X %2.2X %2.2X; %2.2X)",
		    (int)BrushColors,
		    PSS.FillColor.Red, PSS.FillColor.Green, PSS.FillColor.Blue, PSS.FillTransparency);
		}
                else
                  strcpy(cq->ObjType,"!Brush Fore Color");
	      break;
	      }
    case 0x32:{
              WORD R,G,B,A;
              char GradientType;
	      GradientType = fgetc(cq->wpd);
	      WORD BrushColors = 1;
	      if(GradientType!=0)
	         Rd_word(cq->wpd, &BrushColors);

	      if(BrushColors>=1)
		{
                Rd_word(cq->wpd,&R);
                Rd_word(cq->wpd,&G);
                Rd_word(cq->wpd,&B);
                Rd_word(cq->wpd,&A);
		PSS.FillColor.Red = R >> 8;
		PSS.FillColor.Green = G >> 8;
		PSS.FillColor.Blue = B >> 8;
		PSS.FillTransparency = A >> 8;
                sprintf(cq->ObjType,"DP Brush Fore Color n:%d (%X %X %X; %X)",
		    (int)BrushColors, R, G, B, A);
                }                
              else
                strcpy(cq->ObjType,"!DP Brush Fore Color");
              break;
              }
    case 0x33:{
	      PSS.FillBackground.Red = fgetc(cq->wpd);
	      PSS.FillBackground.Green = fgetc(cq->wpd);
	      PSS.FillBackground.Blue = fgetc(cq->wpd);
	      sprintf(cq->ObjType,"Brush Back Color (%2.2X %2.2X %2.2X)",
		  PSS.FillBackground.Red, PSS.FillBackground.Green, PSS.FillBackground.Blue);
	      break;
	      }
    case 0x34:{
	      WORD R,G,B;
              Rd_word(cq->wpd,&R);
              Rd_word(cq->wpd,&G);
              Rd_word(cq->wpd,&B);
              PSS.FillBackground.Red = R >> 8;
	      PSS.FillBackground.Green = G >> 8;
	      PSS.FillBackground.Blue = B >> 8;
              sprintf(cq->ObjType,"DP Brush Back Color (%2X %2X %2X)", R,G,B);
              break;
              }
    case 0x35:{WORD Pattern;
	      Rd_word(cq->wpd,&Pattern);
	      sprintf(cq->ObjType,"Brush Pattern %u", Pattern);
	      switch(Pattern)
	      {
	        case 0: PSS.FillPattern=1; break;	// Solid pattern.
		case 1: PSS.FillPattern=15; break;	// 50% Gray
		case 2: PSS.FillPattern=14; break;	// 25% Gray
		case 3: PSS.FillPattern=13; break;	// 12% Gray
		case 4: PSS.FillPattern=104; break;	// CrossHatch
		case 5: PSS.FillPattern=105; break;	// CrossHatch2
		case 6: PSS.FillPattern=5; break;	// Diagonal CrossHatch
		case 7: PSS.FillPattern=6; break;	// Diagonal CrossHatch7
		case 8: PSS.FillPattern=102; break;	// Horizontal
		case 9: PSS.FillPattern=103; break;	// Horizontal2
		case 10: PSS.FillPattern=8; break;	// Vertical
		case 11: PSS.FillPattern=9; break;	// Vertical
		case 12: PSS.FillPattern=2; break;	// Diagonal up
		case 13: PSS.FillPattern=3; break;	// Diagonal2 up
		case 14: PSS.FillPattern=100; break;	// Diagonal down
		case 15: PSS.FillPattern=101; break;	// Diagonal2 down
		case 16: PSS.FillPattern=106; break;	// Small squares
		case 17: PSS.FillPattern=107; break;	// Bigger squares
		case 26: PSS.FillPattern=109;break;	// Balls
		case 28: PSS.FillPattern=108;break;	// Plus		
		default:if(cq->err && Pattern>=32)
			  {
                          cq->perc.Hide();
		          fprintf(cq->err,_("Warning: Unexpected hatch pattern %d."),Pattern); 
                          }
			PSS.FillPattern=1;
			break;
	      }
	      break;}
    case 0x36:{
              strcpy(cq->ObjType,"Horizontal Line");
              VectorLine *pVecl = new VectorLine(2);
	      SWORD val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[1] = pVecl->Points[3] = val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[0] = val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[2] = val;
              VectList.AddObject(pVecl); 
              break;
              }
    case 0x37:{
              strcpy(cq->ObjType,"Vertical Line");
              VectorLine *pVecl = new VectorLine(2);
	      SWORD val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[0] = pVecl->Points[2] = val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[1] = val;
              Rd_word(cq->wpd, (WORD*)&val);
              pVecl->Points[3] = val;
              VectList.AddObject(pVecl); 
              break;
              }
    case 0x38:strcpy(cq->ObjType,"!Poster Settings");break;
    case 0x39:strcpy(cq->ObjType,"!Image State");
	      PSS.FillColor.Red = 0;	     // I have absolutelly no clue, but it looks like "Image state" RESETs fill foreground color??
	      PSS.FillColor.Green = 0;
	      PSS.FillColor.Blue = 0;
	      PSS.FillTransparency = 0;
	      PSS.FirstTimeFix = false;
	      break;
    case 0x3A:strcpy(cq->ObjType,"!Envelope Definition");break;
    case 0x3B:strcpy(cq->ObjType,"!Envelope");break;
    case 0x3C:{WORD TextureId;
	      Rd_word(cq->wpd,&TextureId);
	      sprintf(cq->ObjType,"!Texture %u Definition",TextureId);
	      break;
	      }
    case 0x3D:strcpy(cq->ObjType,"!Brush Texture");break;
    case 0x3E:strcpy(cq->ObjType,"!Texture Alignment");break;
    case 0x3F:strcpy(cq->ObjType,"!Pen Texture");break;

    default: sprintf(cq->ObjType,"?%d?",(int)Rec2.Type); break;
    }
}


/** This procedure reads both embedded and WPG files.
 * See https://www.fileformat.info/format/wpg/egff.htm
 * @param[in]	Filename	Original name of image. It is used to help generating EPS.
 * 				No file with this name could exist.
 * @param[in]	Box	Image type 0: external; 1: WPG1; 2: WPG2. */
Image LoadEmbeddedPictureWPG(TconvertedPass1 *cq, const char *Filename, TBox & Box, EmbeddedImg **EmImg)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#LoadEmbeddedPictureWPG(%s) ",(Filename==NULL)?"":Filename);fflush(cq->log);
#endif
long ldblk;
char ch,NumFormat;
WPGHeader Hdr;
WPGRecord Rec;
WPG2Record Rec2;
WPG_records WPG;
FILE *SrcImage;
string NewFilename;
Image Img,*CurrImg;
DWORD ResourceEnd, i;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
PS_State PSS;
FloatBBox bbx;
VectorList VectList;
TImgConvStatus ImgSts;

  bbx.MinX=65537; bbx.MaxX=-1; bbx.MinY=65537; bbx.MaxY=-1;

  CurrImg = &Img;  

  if(Box.Image_type==1 && Box.Image_size>0) //WPG level 1
    {
TryWPG1:
    string PSData;

    if(Filename==NULL) Filename=GetSomeImgName(".wpg");
    if(*Filename==0) Filename=GetSomeImgName(".wpg");
    NewFilename = MergePaths(OutputDir,RelativeFigDir)+GetFullFileName(Filename);

    SrcImage=NULL;
    if(SaveWPG>=1)	// make an attempt to extract resource
      {
/*	  SrcImage=fopen(NewFilename.ch,"r");
      if(SrcImage!=NULL)
	     {
	     fclose(SrcImage);
	     goto NoCopyImage;
	     }*/
      if((SrcImage=OpenWrChk(NewFilename,"wb",cq->err))==NULL) goto NoCopyImage;
      Hdr.FileId=0x435057FF;
      Hdr.DataOffset=0x10;
      Hdr.ProductType=0x1601;
      Hdr.FileType=0x1;
      Hdr.MajorVersion=0;
      Hdr.MinorVersion=0;
      Hdr.EncryptKey=0;

      savestruct(SrcImage,"ddwwbbw",
	      Hdr.FileId,Hdr.DataOffset,Hdr.ProductType,Hdr.FileType,
	      Hdr.MajorVersion,Hdr.MinorVersion,Hdr.EncryptKey);
      }

    fseek(cq->wpd,Box.Image_offset,SEEK_SET);
    cq->ActualPos = Box.Image_offset;
    ResourceEnd = Box.Image_offset+Box.Image_size; //end of resource

    Palette = BuildPalette(256,8);
    if(Palette!=NULL)
      for(i=0; i<256; i++)
	 {
	 Palette->R(i,WPG_Palette[i].Red);
	 Palette->G(i,WPG_Palette[i].Green);
	 Palette->B(i,WPG_Palette[i].Blue);
	 }

    while(cq->ActualPos<ResourceEnd)
	{
	Rec.RecType=fgetc(cq->wpd);
	Rd_WP_DWORD(cq->wpd,&Rec.RecordLength);
	if(feof(cq->wpd)) break;
	cq->ActualPos=ftell(cq->wpd);
	DWORD NewObject = cq->ActualPos+Rec.RecordLength;

	NumFormat=0;
	switch(Rec.RecType)
	  {
	  case  0x1:{strcpy(cq->ObjType,"Fill Attributes");
//		     CrackObject(cq,NewObject);//!!!!
		     PSS.FillPattern=fgetc(cq->wpd);
		     BYTE index=fgetc(cq->wpd);
		     PSS.FillColor=WPG_Palette[index];
		     if(Palette!=NULL)
			 {
			 if(index<Palette->Size1D)
			   {
			   PSS.FillColor.Red = Palette->R(index);
			   PSS.FillColor.Green = Palette->G(index);
			   PSS.FillColor.Blue = Palette->B(index);
			   }
		         }
		     break;
		     }
	  case  0x2:{strcpy(cq->ObjType,"Line Attributes");
		   // CrackObject(cq,NewObject);
		      BYTE LineColor,c;
		      WORD LineWidth;

		      c = fgetc(cq->wpd);
		      if(PSS.LineStyle!=c) {PSS.LineStyle=c;PSS.dirty|=PSS_LineStyle;}
		      LineColor = fgetc(cq->wpd);
		      PSS.LineColor = WPG_Palette[LineColor];
		      if(Palette!=NULL)
			 {
			 if(LineColor<Palette->Size1D)
				{
				PSS.LineColor.Red=Palette->R(LineColor);
				PSS.LineColor.Green=Palette->G(LineColor);
				PSS.LineColor.Blue=Palette->B(LineColor);
				}
			 }
		      PSS.dirty|=PSS_LineColor;
		      Rd_word(cq->wpd,&LineWidth);
		      if(fabs(PSS.LineWidth-WPGu2PSu(LineWidth)) > 1e-5)
			{
			PSS.LineWidth = WPGu2PSu(LineWidth);
			PSS.dirty |= PSS_LineWidth;
			}
                      if(Verbosing>=2 && cq->log!=NULL)
                        {
                        fprintf(cq->log, "\nStyle:%u, Color:%u, Width: %u", (unsigned)c, (unsigned)LineColor, (unsigned)LineWidth);
                        }
		      break;
		      }
	  case  0x3:strcpy(cq->ObjType,"!Symbol Attributes"); break;
	  case  0x4:strcpy(cq->ObjType,"!Polysymbol"); break;
	  case  0x5:strcpy(cq->ObjType,"!Line"); break;
	  case  0x6:{strcpy(cq->ObjType,"Curve");
		      Rd_word(cq->wpd,&WPG.Curve.Count);
                      FixPolySize(cq, &WPG.Curve.Count, Rec.RecordLength);
		      float *Points = LoadPoints(cq,WPG.Curve.Count,bbx);
		      if(Points==NULL) break;

                      VectorLine *pVecLine = new VectorLine(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecLine->AttribFromPSS(PSS);  
                      pVecLine->Close = false;                                         
                      pVecLine->Transform(vecResizeXY(WPGu2PSu(1)));
                      VectList.AddObject(pVecLine); pVecLine=NULL;
		      break;
		      }
	  case  0x7:strcpy(cq->ObjType,"!Rectangle"); break;
	  case  0x8:{strcpy(cq->ObjType,"Polygon");
		      Rd_word(cq->wpd,&WPG.Curve.Count);
                      FixPolySize(cq,&WPG.Curve.Count,Rec.RecordLength);
		      float *Points = LoadPoints(cq,WPG.Curve.Count,bbx);
		      if(Points==NULL) break;

                      VectorPolygon *pVecPoly = new VectorPolygon(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecPoly->AttribFromPSS(PSS);  
                      pVecPoly->Close = true;
                      pVecPoly->Transform(vecResizeXY(WPGu2PSu(1)));
                      VectList.AddObject(pVecPoly); pVecPoly=NULL;
		      free(Points);
		      break;
		      }
	  case  0x9:{strcpy(cq->ObjType,"Elipsis");
//		      CrackObject(cq,NewObject);
		      Rd_word(cq->wpd,(WORD*)&WPG.Ellipse.Sx);
		      Rd_word(cq->wpd,(WORD*)&WPG.Ellipse.Sy);
		      Rd_word(cq->wpd,&WPG.Ellipse.rx);
		      Rd_word(cq->wpd,&WPG.Ellipse.ry);
		      Rd_word(cq->wpd,&WPG.Ellipse.RotAngle);
		      Rd_word(cq->wpd,&WPG.Ellipse.bAngle);
		      Rd_word(cq->wpd,&WPG.Ellipse.eAngle);
		      WPG.Ellipse.style = fgetc(cq->wpd);

		      UpdateBBox(bbx, WPG.Ellipse.RotAngle,
				   WPG.Ellipse.Sx-WPG.Ellipse.rx,WPG.Ellipse.Sy-WPG.Ellipse.ry,2*WPG.Ellipse.rx,2*WPG.Ellipse.ry);

                      VectorEllipse *pVecEllipse = new VectorEllipse(WPGu2PSu(WPG.Ellipse.Sy-WPG.Ellipse.ry), WPGu2PSu(WPG.Ellipse.Sy+WPG.Ellipse.ry),
								 WPGu2PSu(WPG.Ellipse.Sx-WPG.Ellipse.rx), WPGu2PSu(WPG.Ellipse.Sx+WPG.Ellipse.rx));
                      pVecEllipse->bAngle = WPG.Ellipse.bAngle;
                      pVecEllipse->eAngle = WPG.Ellipse.eAngle;
                      if(WPG.Ellipse.RotAngle>0)
                      {
                        pVecEllipse->Tx = new vecTransform;
                        pVecEllipse->Tx->RotAngle = WPG.Ellipse.RotAngle;
                      }
                      pVecEllipse->AttribFromPSS(PSS);
                      VectList.AddObject(pVecEllipse);
		      break;
		      }
	  case  0xA:strcpy(cq->ObjType,"!Elipsis"); break;
	  case  0xB:strcpy(cq->ObjType,"Bitmap l1");
		      LoadWPGBitmapType1(cq->wpd,WPG.BitmapType1);
		      if(Raster!=NULL) break;
		      Raster = CreateRaster2D(WPG.BitmapType1.Width,WPG.BitmapType1.Height,WPG.BitmapType1.Depth);

		      if(Raster==NULL) {NoImgMemory(cq,WPG.BitmapType1.Width,WPG.BitmapType1.Height);break;}
		      if(UnpackWPGRaster(Raster,cq->wpd)<0)
				{delete Raster;Raster=NULL;break;}

LoadRaster:	      if(CurrImg->Raster!=NULL)
			{
			CurrImg->Next=new Image;
			CurrImg=CurrImg->Next;
			}
		      ImgSts.RotAngle -= 360*((long)ImgSts.RotAngle/360);
		      CurrImg->x = ImgSts.x;
		      CurrImg->y = ImgSts.y;
		      CurrImg->dx = ImgSts.dx;
		      CurrImg->dy = ImgSts.dy;
		      CurrImg->RotAngle = ImgSts.RotAngle;
		      ImgSts.RotAngle = ImgSts.x = ImgSts.y = ImgSts.dx = ImgSts.dy = 0;
		      CurrImg->Raster=Raster;CurrImg->Raster->UsageCount++;Raster=NULL;
		      if(CurrImg->Raster->GetPlanes()>1)
                          AssignPalette(CurrImg,Palette);
		      break;
	  case  0xC:strcpy(cq->ObjType,"!Graphics Text");NumFormat=1; break; //This is a bug fix for Draw Perfect WPG
	  case  0xD:{strcpy(cq->ObjType,"Text Attributes");
		      BYTE TextColor;
		      fseek(cq->wpd, 19, SEEK_CUR);
		      TextColor = fgetc(cq->wpd);
		      PSS.TextColor = WPG_Palette[TextColor];
		      break;
		      }
	  case  0xE:strcpy(cq->ObjType,"Color Map");
		      LoadWPGColormap(cq->wpd,WPG.ColorMapRec);
		      if(Palette!=NULL && Palette->UsageCount==0) delete Palette;
		      Palette=BuildPalette(WPG.ColorMapRec.NumOfEntries+WPG.ColorMapRec.StartIndex,8);
		      if(Palette!=NULL)
			 {
			 fread((char *)Palette->Data1D + 3*WPG.ColorMapRec.StartIndex,
				3*WPG.ColorMapRec.NumOfEntries,1,cq->wpd);
			  }
		      break;	// NumFormat=1; // default DR
	  case  0xF:strcpy(cq->ObjType,"!Start WPG l1"); break;
	  case 0x10:strcpy(cq->ObjType,"!End WPG l1"); break;
	  case 0x11:strcpy(cq->ObjType,"Start PS l1");
		      if(Rec.RecordLength>8)
			{
			if(EmImg!=NULL)
			  {
			  EmbeddedImg *Ei2 = (EmbeddedImg *)malloc(sizeof(EmbeddedImg));
			  Ei2->Offset=cq->ActualPos+8;   /*skip PS header in the wpg*/
			  Ei2->Length=Rec.RecordLength-8;
			  Ei2->Extension=Ei2->ImgName=NULL;
			  Ei2->Next=*EmImg;
			  *EmImg=Ei2;
			  }
			}
		      break;
	  case 0x12:strcpy(cq->ObjType,"!Output Attributes"); break;
	  case 0x13:{strcpy(cq->ObjType,"Plain Curve");
		      fseek(cq->wpd, 4, SEEK_CUR);
		      
		      Rd_word(cq->wpd,&WPG.Curve.Count);
                      FixPolySize(cq, &WPG.Curve.Count, Rec.RecordLength);
                      float *Points = LoadPoints(cq, WPG.Curve.Count, bbx);
                      if(Points==NULL) break;

                      VectorLine *pVecLine;
		      if((WPG.Curve.Count-1)%3 == 0)
                        pVecLine = new VectorCurve(Points, WPG.Curve.Count);                        
		      else		// Curve is deffective, plot at least polyline.
                        pVecLine = new VectorLine(Points, WPG.Curve.Count);

                      pVecLine->AttribFromPSS(PSS);  
                      pVecLine->Transform(vecResizeXY(WPGu2PSu(1)));
                      VectList.AddObject(pVecLine);
		      break;
		      }
	  case 0x14:{
		      strcpy(cq->ObjType,"Bitmap l2");
		      LoadWPGBitmapType2(cq->wpd,WPG.BitmapType2);
		      if(Raster!=NULL) break;
		      Raster=CreateRaster2D(WPG.BitmapType2.Width,WPG.BitmapType2.Height,WPG.BitmapType2.Depth);
		      if(Raster==NULL) {NoImgMemory(cq,WPG.BitmapType2.Width,WPG.BitmapType2.Height);break;}
		      ImgSts.x = WPG.BitmapType2.LowLeftX/47.0;
		      ImgSts.y = WPG.BitmapType2.LowLeftY/47.0;
		      ImgSts.dx = (WPG.BitmapType2.UpRightX-WPG.BitmapType2.LowLeftX)/47.0;
		      ImgSts.dy = (WPG.BitmapType2.UpRightY-WPG.BitmapType2.LowLeftY)/47.0;
		      ImgSts.RotAngle = WPG.BitmapType2.RotAngle & 0x0FFF;
		      if(UnpackWPGRaster(Raster,cq->wpd)<0)
				{delete Raster;Raster=NULL;break;}

		      if(WPG.BitmapType2.RotAngle & 0x8000)
			   {Flip1D(Raster);ImgSts.RotAngle = 360-ImgSts.RotAngle;}
		      if(WPG.BitmapType2.RotAngle & 0x2000)
			   {
			   Flip2D(Raster);
			   if((WPG.BitmapType2.RotAngle & 0x8000)==0)
				ImgSts.RotAngle = 360-ImgSts.RotAngle;
			   }
		      goto LoadRaster;
		      }
	  case 0x15:strcpy(cq->ObjType,"!Start Image"); break;
	  case 0x16:strcpy(cq->ObjType,"!Start Graph"); break;
	  case 0x17:strcpy(cq->ObjType,"!Plan Perfect"); break;
	  case 0x18:  // NumFormat=1; // default DR
		      //  CrackObject(cq,NewObject);
		      ldblk = Rec.RecordLength-21;
		      if(ldblk>0)
			{
			fseek(cq->wpd,cq->ActualPos+4,SEEK_SET);
			Rd_word(cq->wpd,&WPG.TextL2.RotAngle);
			WPG.TextL2.RotAngle = WPG.TextL2.RotAngle%360;
			fseek(cq->wpd,cq->ActualPos+8,SEEK_SET);
			Rd_word(cq->wpd,(WORD *)&WPG.TextL2.LowLeftX);
			Rd_word(cq->wpd,(WORD *)&WPG.TextL2.LowLeftY);
			Rd_word(cq->wpd,(WORD *)&WPG.TextL2.UpRightX);
			Rd_word(cq->wpd,(WORD *)&WPG.TextL2.UpRightY);
			// [XScale] [YScale] (Type)

				// Debug purpose rectangle
			/* VectorRectangle *Vr = new VectorRectangle(WPGu2PSu(WPG.TextL2.LowLeftY), WPGu2PSu(WPG.TextL2.UpRightY), WPGu2PSu(WPG.TextL2.LowLeftX), WPGu2PSu(WPG.TextL2.UpRightX));
			Vr->AttribFromPSS(PSS);
			VectList.AddObject(Vr); */
			
			UpdateBBox(bbx, WPG.TextL2.RotAngle,
				   WPG.TextL2.LowLeftX,WPG.TextL2.LowLeftY,WPG.TextL2.UpRightX-WPG.TextL2.LowLeftX,WPG.TextL2.UpRightY-WPG.TextL2.LowLeftY);
			Textl2_2PS(cq,VectList,ldblk,PSS,&WPG);
			}
		      strcpy(cq->ObjType,"Graphics Text l2");
		      break;
	  case 0x19:strcpy(cq->ObjType,"!Data Start l2");  break;
	  case 0x1A:strcpy(cq->ObjType,"!Graphics Text l3"); break;
	  case 0x1B:strcpy(cq->ObjType,"PostScript l2");
		    if(Rec.RecordLength>0x3B)
		      if(EmImg!=NULL)
		        {
		        EmbeddedImg *Ei2 = (EmbeddedImg *)malloc(sizeof(EmbeddedImg));
			Ei2->Offset = cq->ActualPos+0x3C;   /*skip PS l2 header in the wpg*/
		        Ei2->Length = Rec.RecordLength-0x3C;
		        Ei2->Extension = Ei2->ImgName=NULL;
		        Ei2->Next = *EmImg;
		        *EmImg = Ei2;
			}
		      break;
	  default: sprintf(cq->ObjType,"?%d?",(int)Rec.RecType); break;
	  }

	//Save WPG to the disk file
    if(SrcImage)
	      {
	      fseek(cq->wpd,cq->ActualPos,SEEK_SET);
	      fputc(Rec.RecType,SrcImage);
	      Wr_WP_DWORD(SrcImage, Rec.RecordLength, NumFormat);
	      for(i=0; i<Rec.RecordLength; i++)
		      {
		      ch = fgetc(cq->wpd);
		      if(feof(cq->wpd)) break;
		      fputc(ch,SrcImage);
		      }
	      }

	//Log a graphical image object into report
	    if(cq->log!=NULL)
		    {
		    fprintf(cq->log,"\n%*s{GRtyp#%Xh;len:%lXh;%s}",cq->recursion * 2, "",
		       (int)Rec.RecType,(long)Rec.RecordLength,cq->ObjType);
		    #ifdef DEBUG
		    fflush(cq->log);
		    #endif //DEBUG
		    }
	    if(*cq->ObjType=='!') {UnknownObjects++;*cq->ObjType=0;}

	    if(NewObject!=ftell(cq->wpd))
		    fseek(cq->wpd,NewObject,SEEK_SET);
	    cq->ActualPos=NewObject;
	    } /**/

    if(SrcImage) {fclose(SrcImage);SrcImage=NULL;}
    Box.Image_size = 0;

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

  if(Box.Image_type==2 && Box.Image_size>0) //WPG level 2 (might contain l.1 or different content)
    {
    WPG2Start StartWPG2;
    //float_matrix CTM(3,3);

    if(Filename==NULL || *Filename==0)
        Filename=GetSomeImgName(".wpg");
    NewFilename = MergePaths(OutputDir,RelativeFigDir)+GetFullFileName(Filename);

    fseek(cq->wpd,Box.Image_offset,SEEK_SET);
    ResourceEnd = Box.Image_offset+Box.Image_size; //end of resource

    loadstruct(cq->wpd,"ddwwbbw",
	      &Hdr.FileId,&Hdr.DataOffset,&Hdr.ProductType,&Hdr.FileType,
	      &Hdr.MajorVersion,&Hdr.MinorVersion,&Hdr.EncryptKey);
    if(Hdr.FileId!=0x435057FF || Hdr.EncryptKey!=0) goto NoCopyImage;

    if((Hdr.ProductType>>8)==0x30)
      {
      cq->perc.Hide();
      if(SaveWPG>=1)
        {
	printf("\n!!!! MAC WPG1 detected!!!! %X",Box.Image_offset);
        if((SrcImage=OpenWrChk(NewFilename,"wb",cq->err))==NULL)
	   {
	   if(cq->err!=NULL && Verbosing>=0)
	       {
	       cq->perc.Hide();
	       fprintf(cq->err, _("\nError: Cannot write to the file %s !"),NewFilename());
	       }
	   goto NoCopyImage;
	   }
        fseek(cq->wpd,Box.Image_offset,SEEK_SET);
        for(i=0;i<Box.Image_size;i++)
	   {
	   ch = fgetc(cq->wpd);
	   //if(feof(cq->wpd)) putchar('!');
	   fputc(ch,SrcImage);
	   }
        fclose(SrcImage);
	//printf("...extracted.%X......",ftell(cq->wpd));
        }

      cq->ActualPos = Box.Image_offset+0x10;
      fseek(cq->wpd, cq->ActualPos, SEEK_SET);
      while(cq->ActualPos<ResourceEnd)
	{
	RdDWORD_HiEnd(&Rec2.RecordLength, cq->wpd);
	DWORD NewObject = ftell(cq->wpd) + Rec2.RecordLength;
	Rec2.Class = fgetc(cq->wpd);
	Rec2.Type = fgetc(cq->wpd);

	switch(Rec2.Type)
	  {
	  case 0:   strcpy(cq->ObjType,"!Start WPG MAC"); break;
	  case 0x11:strcpy(cq->ObjType,"!Graphics Text"); break;
	  default:  cq->ObjType[0] = 0;
	  }

	if(cq->log!=NULL)
	  {
	  fprintf(cq->log,"\n%*s{GRtyp#%Xh;len:%lXh;%s}",cq->recursion * 2, "",
		     (int)Rec2.Type, (long)Rec2.RecordLength, cq->ObjType);
		    //fprintf(cq->log,"MaxY= %2.2f",MaxY);
	  fprintf(cq->log," Pos= %lX", (long)cq->ActualPos);
	  }
	cq->ActualPos = (NewObject+1) & ~1;
        fseek(cq->wpd,cq->ActualPos,SEEK_SET);
	}
      goto NoCopyImage;
      }

    if((Hdr.ProductType>>8)!=0x16)  goto NoCopyImage;
    if(Hdr.FileType==1)  /*WPG level 1 with header*/
	{
	Box.Image_offset+=Hdr.DataOffset;
	Box.Image_size-=Hdr.DataOffset;
	if(Box.Image_size>0) goto TryWPG1;
	}
    if(Hdr.FileType!=2) goto NoCopyImage;

    if(SaveWPG>=1)	// Make an attempt to extract resource to the separate file.
      {
      if((SrcImage=OpenWrChk(NewFilename(),"wb",NULL))==NULL)	// the error will be output later.
	   {
	   if(cq->err!=NULL && Verbosing>=0)
	       {
	       cq->perc.Hide();
	       fprintf(cq->err, _("\nError: Cannot extract image to the file '%s'!"),NewFilename());
	       }	   
	   }
      else	// SrcImage != NULL
          {
          fseek(cq->wpd,Box.Image_offset,SEEK_SET);
          CopyFile(SrcImage, cq->wpd, Box.Image_size);          
          fclose(SrcImage);

          SrcImage = fopen(NewFilename(),"rb");
          if(SrcImage)
            {
            CheckFileFormat(SrcImage,FilForD);
            fclose(SrcImage);
            
            const char *Ext = GetExtension(NewFilename());
            if(FilForD.Extension!=NULL && Ext!=NULL)
              {
              if(*Ext=='.') Ext++;
              if(stricmp(Ext,FilForD.Extension))
                {
                if(cq->err!=NULL && Verbosing>=0)
	          {
	          cq->perc.Hide();
	          fprintf(cq->err, _("\nError: File '%s' has wrong extension, should be used '%s'!"), NewFilename(), FilForD.Extension);
                  }
                string FileNameFix(copy(NewFilename(),0,Ext-NewFilename()) + FilForD.Extension);
                rename(NewFilename(), FileNameFix());
                NewFilename = FileNameFix;
                }
	     }            
           }
         }
      }

    fseek(cq->wpd,Box.Image_offset+Hdr.DataOffset,SEEK_SET);
    cq->ActualPos = Box.Image_offset+Hdr.DataOffset;
    StartWPG2.PosSizePrecision = 0;
    PSS.LineStyle = 0;		// No contour line as default.
    PSS.FirstTimeFix = true;
    memset(&PSS.FillColor, 0xFF, sizeof(PSS.FillColor));
    memset(&PSS.FillBackground, 0xFF, sizeof(PSS.FillBackground));

    //VectorList VectList;
    string PSData;
    while(cq->ActualPos<ResourceEnd)
	    {
	    Rec2.Class = fgetc(cq->wpd);
	    Rec2.Type = fgetc(cq->wpd);
	    Rd_WP_DWORD(cq->wpd,&Rec2.Extension);
	    Rd_WP_DWORD(cq->wpd,&Rec2.RecordLength);
	    cq->ActualPos = ftell(cq->wpd);
	    DWORD NewObject = cq->ActualPos+Rec2.RecordLength;

	    if(feof(cq->wpd)) break;

            ProcessWPG2Token(cq, Rec2, StartWPG2, Img, /*CTM,*/ &Palette,
	      PSData, PSS, EmImg, NewObject, ResourceEnd, &CurrImg,
	      ImgSts, bbx, VectList);

		//Log a graphical image object into report
	    if(cq->log!=NULL)
		    {
		    fprintf(cq->log,"\n%*s{GRtyp#%X;len:%lXh;%s}", cq->recursion*2, "",
		       (int)Rec2.Type, (long)Rec2.RecordLength, cq->ObjType);
		    //fprintf(cq->log,"MaxY= %2.2f",MaxY);
		    fprintf(cq->log," Pos= %lX", (long)cq->ActualPos);
		    }
	    if(*cq->ObjType=='!') {UnknownObjects++;*cq->ObjType=0;}

	    if(NewObject!=ftell(cq->wpd))
		    fseek(cq->wpd,NewObject,SEEK_SET);
	    cq->ActualPos = NewObject;
	    }

    if(PSData.length() > 0)
        VectList.AddObject(new PsBlob(PSData.ExtractString()));    
    Box.Image_size=0;
    }

 if(VectList.VectorObjects > 0)		//Transfer PostScript part into Image
   {
   if(Img.Raster!=NULL || Img.VecImage!=NULL)
	{
	CurrImg->Next = new Image;
	CurrImg = CurrImg->Next;
	}
   if(CurrImg!=NULL)
	{
	CurrImg->x = bbx.MinX/47.0;
	CurrImg->y = bbx.MinY/47.0;
	CurrImg->dx=(bbx.MaxX-bbx.MinX)/47.0;
	CurrImg->dy=(bbx.MaxY-bbx.MinY)/47.0;
	CurrImg->RotAngle = 0;
	CurrImg->AttachVecImg(new VectorImage(VectList,PSS));
	}
   }

 if(Palette!=NULL && Palette->UsageCount==0) {delete Palette;Palette=NULL;}
 if(Raster!=NULL && Raster->UsageCount==0) {delete Raster;Raster=NULL;}

NoCopyImage:
return Img;
}


/** This is replacement for LoadPicture for the wp2latex purposes. */
Image WP2L_LoadPictureWPG(TconvertedPass1 *cq, const char *FileName)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#WP2L_LoadPictureWPG(%s) ",FileName);fflush(cq->log);
#endif
TBox Box;
FILE *WPD_Backup,*f;
DWORD ActualPos_Backup;
EmbeddedImg *EmImTmp, *EmImg=NULL;
Image Img;
SBYTE Bk_SaveWPG;

  if((f=fopen(FileName,"rb"))==NULL) return(NULL);
  if(cq->log) fprintf(cq->log,_("\nOpening external image %s "),FileName);

  WPD_Backup = cq->wpd;
  ActualPos_Backup = cq->ActualPos;

  cq->wpd = f;
  cq->ActualPos = 0;

  initBox(Box);
  Box.HorizontalPos = 3;	// 3-Full
  Box.Image_offset= 0;
  Box.Image_size  = filesize(cq->wpd);  // -Box.Image_offset
  Box.Image_type=2;		// WPG l2 embedded parser has also autodetect for WPG l.1
  Box.Contents = 3; 		// content image - every images are internally converted into WPG

  Bk_SaveWPG = SaveWPG;
  if(SaveWPG>=1) SaveWPG=0;		// block save of current WPG.
  Img = LoadEmbeddedPictureWPG(cq,FileName,Box,&EmImg);

  while(EmImg!=NULL)	//embedded PS and other files should be processed here
     {
     if(EmImg->Length>0)
       if(cq->err!=NULL && Verbosing>=0)
         {
         cq->perc.Hide();
         fprintf(cq->err, _("\nError: Embedded image on pos %Xh, size %u discarded!"), EmImg->Offset, EmImg->Length);

         if(EmImg->Offset>8) /*Try to extract PS part from WPG metafile*/
	   {
	   /* fseek(cq->wpd,EmImg->Offset,SEEK_SET);
              if(CheckWPG2WithoutPrefix(cq->wpd,EmImg->Length)) //TODO: Detect WPG2 without preffix here.
              {
              } */
           string FragmentFileName = MergePaths(OutputDir,RelativeFigDir);
	   string NewFilename = FragmentFileName;
	   FragmentFileName += GetSomeImgName(".tmp");           

           fseek(cq->wpd,EmImg->Offset,SEEK_SET);

	   if(CopyFile(FragmentFileName(),cq->wpd,EmImg->Length)>=-1)
	     {	
	     EmImg->Length = 0;		// Invalidate fragment.

	     FILE *SrcImage;
             if((SrcImage=fopen(FragmentFileName(),"rb"))==NULL)
	       {
               if(cq->err)
	           fprintf(cq->err, _("\nError: Cannot open file %s for reading!"),FragmentFileName());
	       goto FileIsOK;
               }

	     CheckFileFormat(SrcImage,FilForD);
	     if(!strcmp(FilForD.Converter,"EPS") || !strcmp(FilForD.Converter,"PS"))
	       {             //EPS is detected
               fclose(SrcImage);
               SrcImage = NULL;
	       NewFilename += GetSomeImgName(".eps");
               rename(FragmentFileName(), NewFilename());
               goto FileIsOK;
               }

             if(FilForD.Converter==NULL || *FilForD.Converter==0) 
                 {fclose(SrcImage);SrcImage=NULL;}
             else
               {
	       if(*FilForD.Converter==0) {fclose(SrcImage);SrcImage=NULL;}
               }

	     if(SrcImage)
               {
	       TconvertedPass1 *cq1_new = GetConverter(FilForD.Converter);
	       if(cq1_new!=NULL)
	         {
/*
	         if(Verbosing >= 1) printf(_("[nested \"%s\" %s] "),NewFilename(),FilForD.Converter);
	         cq1_new->InitMe(SrcImage,cq->table,cq->strip,cq->log,cq->err);
	         cq1_new->recursion = cq->recursion+1;
                 static const int Value1 = 1;
	         cq1_new->Dispatch(DISP_NOCONVERTIMAGE,&Value1);
	         i = cq1_new->Convert_first_pass();
	         if(Verbosing >= 1) printf(_("\n[continuing] "));
	         if(cq->log!=NULL) fputs(_("\n--End or nested file.--\n"),cq->log);
	         cq->perc.displayed = false;

	         Image *pImg=NULL;
	         cq1_new->Dispatch(DISP_EXTRACTIMAGE,&pImg);

	         if(pImg)
                    {
 	            if(pImg->Raster!=NULL || pImg->VecImage!=NULL)  //Convert raster image into the PostScript
	              {		//here should be called reduction of colors
#ifndef NO_IMG
		      ReducePalette(pImg,256);
#endif                  
		      SavePictureEPS_MT(NewFilename,*pImg,cq->err,&cq->perc);		 
		      bFileIsOK = true;                  
                      }
		    delete pImg;
                    }
*/
	          delete cq1_new;
	          }
	          else
	             fprintf(cq->err, _("\nError: Conversion module \"%s\" is not available for file %s!"),FilForD.Converter,NewFilename());

	         fclose(SrcImage);

                 if(SaveWPG<0)
                     unlink(FragmentFileName());
                 else
                   {
                   string FixFragmentExtension = copy(FragmentFileName,0,FragmentFileName.length()-3) + FilForD.Extension;
                   rename(FragmentFileName(), FixFragmentExtension());
                   }
                 }  // SrcImage==NULL

               }
             else
	        fprintf(cq->err, _("\nError: Cannot detect fileformat of file %s!"), FragmentFileName());

           }  // EmImg->Offset>8
         }
FileIsOK:
     EmImTmp = EmImg->Next;
     free(EmImg);
     EmImg = EmImTmp;
     }

  SaveWPG = Bk_SaveWPG;

  fclose(f);
  cq->wpd=WPD_Backup;
  cq->ActualPos=ActualPos_Backup;

  if(Img.Raster==NULL && Img.VecImage==NULL)   // last chance - try to load any file format
        Img = LoadPicture(FileName);

  return Img;
}


/** Patch for LoadPicture that allows to use advanced WPG support. */
inline Image WP2L_LoadPicture(TconvertedPass1 *cq, const char *Name)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#WP2L_LoadPicture(%s) ",Name);fflush(cq->log);
#endif
Image Img;
   Img = WP2L_LoadPictureWPG(cq,Name);
   if(Img.Raster!=NULL || Img.VecImage!=NULL) return(Img);
return LoadPicture(Name);
}


/** Check whether file contains WPG2 stream of objects without header. */
bool CheckWPG2WithoutPrefix(FILE *F, size_t BlobSize)
{
WPG2Record Rec2;

  //Rec2.Class = fgetc(F);
  Rec2.Type = fgetc(F);
  Rd_WP_DWORD(F,&Rec2.Extension);
  Rd_WP_DWORD(F,&Rec2.RecordLength);

  if(Rec2.RecordLength > BlobSize) return false;
  if(Rec2.Type != 1) return false;

  fseek(F,Rec2.RecordLength,SEEK_CUR);

  Rec2.Type = fgetc(F);
  Rd_WP_DWORD(F,&Rec2.Extension);
  Rd_WP_DWORD(F,&Rec2.RecordLength);

  return true;
}


/** Main procedure that extracts WPG blocks from WP files and also handles external files. */
void ImageWP(TconvertedPass1 *cq, const char *Filename, TBox & Box)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ImageWP(%s) ",Filename);fflush(cq->log);
#endif
int i;
int lines=0;
const char *ss;
string NewFilename;
string FileExtension;
Image Img;
EmbeddedImg *EmImTmp,*EmImg=NULL;
FILE *SrcImage;
bool bFileIsOK = false;

  if(InputPS<false) return;  /* The PS image feature is disabled */

  if(Filename==NULL || *Filename==0)
      Filename=GetSomeImgName(".wpg"); //invent some filename if empty

  if(Filename)
  {
    if(!strcmp(Filename,"*OLE*"))
	{
	Filename = GetSomeImgName(".wpg"); //fix for embedded OLE objects
	}
  }

  Img = LoadEmbeddedPictureWPG(cq,Filename,Box,&EmImg);

  NewFilename = MergePaths(OutputDir,RelativeFigDir)+CutFileName(Filename)+".eps";
  NewFilename = replacesubstring(NewFilename,"?","Q");

  ss = GetExtension(Filename);
  if(ss!=NULL) if(*ss!=0) FileExtension=ToUpper(ss+1);

  if(NewFilename!="")
     for(i=StrLen(OutputDir);i<length(NewFilename);i++)
	{
	if(NewFilename[i]==' ' || NewFilename[i]=='#' || NewFilename[i]=='%' || NewFilename[i]=='$' || NewFilename[i]=='*')
		{
		if(cq->err != NULL)
		  {
		  cq->perc.Hide();
		  fprintf(cq->err, _("\nWarning: LaTeX cannot process filenames that contain '%c'-> fixed to '_'!"),NewFilename[i]);
		  }
		NewFilename[i]='_';
		}
	}

  if(FileExtension=="PS" || FileExtension=="EPS")
     {			// Chybi verifikace na ps!!!!!!!
     if(AbsolutePath(Filename))
	{
	if(!CopyFile(NewFilename,Filename))
          {
          bFileIsOK = true;
          goto FileIsOK;
          }
	}
     else
	{
	for(i=0; i<length(ImgInputDirs); i++)
	  {
	  if(!CopyFile(NewFilename,(string(ImgInputDirs[i])+Filename)())) 
            {
            bFileIsOK = true;
            goto FileIsOK;
            }
	  }
	}
     }

  if(Img.Raster!=NULL || Img.VecImage!=NULL)	// Convert raster image into the PostScript
	{
SavePS: 
#ifndef NO_IMG
	ReducePalette(&Img,256);        
#endif
	SavePictureEPS_MT(NewFilename, Img, cq->err, &cq->perc);
        bFileIsOK = true;	
	}

  while(EmImg!=NULL && !bFileIsOK)
     {
     if(EmImg->Offset>8 && EmImg->Length>0) /*Try to extract PS part from WPG metafile*/
	{
/*        fseek(cq->wpd,EmImg->Offset,SEEK_SET);
        if(CheckWPG2WithoutPrefix(cq->wpd,EmImg->Length)) //TODO: Detect WPG2 without preffix here.
        {
        } */

        string FragmentFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(".tmp");
        fseek(cq->wpd,EmImg->Offset,SEEK_SET);
	if(CopyFile(FragmentFileName(),cq->wpd,EmImg->Length)>=-1)
	  {	
	  EmImg->Length = 0;		// Invalidate fragment.

	  if((SrcImage=fopen(FragmentFileName(),"rb"))==NULL)
	    {
            if(cq->err)
	      fprintf(cq->err, _("\nError: Cannot open file %s for reading!"), FragmentFileName());
	    goto FileIsOK;
            }

	  CheckFileFormat(SrcImage,FilForD);
	  if(!strcmp(FilForD.Converter,"EPS") || !strcmp(FilForD.Converter,"PS"))
	    {             //EPS is detected
            fclose(SrcImage);
            SrcImage = NULL;            
            rename(FragmentFileName(), NewFilename());
            goto FileIsOK;
            }

          if(FilForD.Converter==NULL || *FilForD.Converter==0) 
            {fclose(SrcImage);SrcImage=NULL;}
          else
            {
	    if(*FilForD.Converter==0) {fclose(SrcImage);SrcImage=NULL;}
            }

	  if(SrcImage)
            {
	    TconvertedPass1 *cq1_new = GetConverter(FilForD.Converter);
	    if(cq1_new!=NULL)
	      {
	      if(Verbosing >= 1) printf(_("[nested \"%s\" %s] "),NewFilename(),FilForD.Converter);
	      cq1_new->InitMe(SrcImage,cq->table,cq->strip,cq->log,cq->err);
	      cq1_new->recursion = cq->recursion+1;
              static const int Value1 = 1;
	      cq1_new->Dispatch(DISP_NOCONVERTIMAGE,&Value1);
	      i = cq1_new->Convert_first_pass();
	      if(Verbosing >= 1) printf(_("\n[continuing] "));
	      if(cq->log!=NULL) fputs(_("\n--End or nested file.--\n"),cq->log);
	      cq->perc.displayed = false;

	      Image *pImg=NULL;
	      cq1_new->Dispatch(DISP_EXTRACTIMAGE,&pImg);

	      if(pImg)
                {
 	        if(pImg->Raster!=NULL || pImg->VecImage!=NULL)  /*Convert raster image into the PostScript*/
	          {		//here should be called reduction of colors
#ifndef NO_IMG
		  ReducePalette(pImg,256);
#endif                  
		  SavePictureEPS_MT(NewFilename,*pImg,cq->err,&cq->perc);		 
		  bFileIsOK = true;                  
                  }
		delete pImg;
                }

	      delete cq1_new;
	      }
	    else
	       fprintf(cq->err, _("\nError: Conversion module \"%s\" is not available for file %s!"),FilForD.Converter,NewFilename());
	    fclose(SrcImage);

            if(SaveWPG<0)
              unlink(FragmentFileName());
            else
              {
              string FixFragmentExtension = copy(FragmentFileName,0,FragmentFileName.length()-3) + FilForD.Extension;
              rename(FragmentFileName(), FixFragmentExtension());
              }
            }
          else
	    fprintf(cq->err, _("\nError: Cannot detect fileformat of file %s!"),NewFilename());          
	  }
	}
    EmImTmp = EmImg->Next;
    free(EmImg);
    EmImg = EmImTmp;
    }

	/* Try to load image from external file */
  if(!bFileIsOK)
    {
    if(AbsolutePath(Filename))
       {
       Img = WP2L_LoadPicture(cq,Filename);
       if(Img.Raster!=NULL) goto SavePS;
       }
    else
       for(i=0;i<length(ImgInputDirs);i++)
	 {
         string FnamePath = string(ImgInputDirs[i]) + Filename;
#if defined(__MSDOS__) || defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) || defined(__OS2__)
         FnamePath = replacesubstring(FnamePath, "/","\\");
#else
         FnamePath = replacesubstring(FnamePath, "\\","/");
#endif
	 Img = WP2L_LoadPicture(cq,FnamePath());
	 if(Img.Raster!=NULL) goto SavePS;
	}
   
   if(!CheckPreviousPS(NewFilename))
     {
     MakeDummyPS(Filename,Img);
     if(Img.VecImage!=NULL) goto SavePS;     
     }
   }

FileIsOK:
  while(EmImg!=NULL)		// Check for remaining EmImg fragments and erase them.
     {
     if(EmImg->Length>0)
       {
       printf(_("\nResource 'embedded image' is lost, no support of multiple scenes, please fix."));
       if(SaveWPG>=0)
         {
         string FragmentFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(".tmp");
         fseek(cq->wpd,EmImg->Offset,SEEK_SET);
         if(CopyFile(FragmentFileName(),cq->wpd,EmImg->Length)>=-1)
           {
           if((SrcImage=fopen(FragmentFileName(),"rb"))!=NULL)
             {
             CheckFileFormat(SrcImage,FilForD);
             fclose(SrcImage);
             if(FilForD.Converter!=NULL && *FilForD.Converter!=0)
               {
               string FixFragmentExtension = copy(FragmentFileName,0,FragmentFileName.length()-3) + FilForD.Extension;
               rename(FragmentFileName(), FixFragmentExtension());
               }
             }
           }
         }
       }
     EmImTmp = EmImg->Next;
     free(EmImg);
     EmImg = EmImTmp;
     }

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

  if(cq->char_on_line == -20)    /* Left one enpty line for new enviroment */
	{
	fputc('%', cq->strip);
	NewLines(cq,2);
	}
  if(cq->char_on_line==true)    /* make new line for leader of minipage */
	NewLines(cq,1);

  if(cq->flag == HeaderText || cq->envir=='B') Box.AnchorType=2;

//This writes a TeX program for including an image into the document
  InputPS|=1;
  if(Box.AnchorType!=2)
    {
    if(!BoxTexHeader(cq, Box))
      {
      if(Box.CaptionSize>0 && Box.CaptionPos>0)
        {
          if(cq->err!=NULL && Verbosing>=0)
	  {
	    cq->perc.Hide();
	    fprintf(cq->err, _("\nError: Existing caption cannot be inserted into a current order of objects."));
	  }
        }
      Box.CaptionSize = 0;
      }
    putc('\n',cq->strip); lines++;
    }

  if(InputPS & IMG_graphicx)	//graphicx.sty
    {
    if(Box.AnchorType!=2) fprintf(cq->strip," \\begin{center}");
    if(fabs(Box.HScale-1)>1e-4 || fabs(Box.VScale-1)>1e-4)
        fprintf(cq->strip," \\scalebox{%2.2f}[%2.2f]{",Box.HScale,Box.VScale);
    if(Box.RotAngle!=0)
        fprintf(cq->strip," \\rotatebox{%d}{",Box.RotAngle);
    fprintf(cq->strip,"\\includegraphics");
    switch(Box.HorizontalPos)
	{
	case 4:if(Box.Width>0)
		   {
		   fprintf(cq->strip,"[width=%2.2f\\textwidth]",Box.Width/100);break;
		   }
	case 3:{
               bool height_width = (Box.RotAngle>88 && Box.RotAngle<92) ||
                                   (Box.RotAngle>268 && Box.RotAngle<272);
               fprintf(cq->strip,"[%s=\\textwidth]", height_width?"height":"width");
	       break;
               }
	default:fprintf(cq->strip,"[width=%2.2fcm]",
		  (Box.Width>0) ? (Box.Width/10.0) : DEFAULT_BOX_WIDTH);
	}
    fprintf(cq->strip,"{\\FigDir/%s.eps}",NewFilename() );
    if(Box.RotAngle!=0) fprintf(cq->strip,"}");
    if(fabs(Box.HScale-1)>1e-4 || fabs(Box.VScale-1)>1e-4) fprintf(cq->strip,"}");
    if(Box.AnchorType!=2) fprintf(cq->strip," \\end{center}");
    putc('\n',cq->strip); lines++;
    }
  else if(InputPS & 4)	//epsfig.sty
    {
    if(Box.AnchorType!=2) fprintf(cq->strip," \\begin{center}");
    fprintf(cq->strip,"\\epsfig{file=\\FigDir/%s.eps,",NewFilename() );
    switch(Box.HorizontalPos)
	{
	case 4:if(Box.Width>0)
		   {
		   fprintf(cq->strip,"width=%2.2f\\textwidth",Box.Width/100);break;
		   }
	case 3:fprintf(cq->strip,"width=\\textwidth");break;
	default:fprintf(cq->strip,"width=%2.2fcm",
                   (Box.Width>0) ? (Box.Width/10.0) : DEFAULT_BOX_WIDTH);
	}
    if(Box.RotAngle!=0) fprintf(cq->strip,",angle=%d",Box.RotAngle);
    putc('}',cq->strip);
    if(Box.AnchorType!=2) fprintf(cq->strip," \\end{center}");
    putc('\n',cq->strip); lines++;
    }
  else if(InputPS & IMG_graphics)	//graphics.sty
    {
    if(Box.AnchorType!=2) fprintf(cq->strip," \\begin{center}");
    if(fabs(Box.HScale-1)>1e-4 || fabs(Box.VScale-1)>1e-4)
		fprintf(cq->strip," \\scalebox{%2.2f}[%2.2f]{",Box.HScale,Box.VScale);
    else
      {
      if(Box.Width>0 && Box.HorizontalPos!=3)
          fprintf(cq->strip,"\\resizebox{%2.2fcm}{!}{", Box.Width/10.0);
      }
    if(Box.RotAngle!=0) fprintf(cq->strip," \\rotatebox{%d}{",Box.RotAngle);
    fprintf(cq->strip,"\\includegraphics");
    fprintf(cq->strip,"{\\FigDir/%s.eps}",NewFilename() );
    if(Box.RotAngle!=0) fprintf(cq->strip,"}");
    if(fabs(Box.HScale-1)>1e-4 || fabs(Box.VScale-1)>1e-4) 
	fprintf(cq->strip,"}");
    else
      {
      if(Box.Width>0 && Box.HorizontalPos!=3)
          fprintf(cq->strip,"}");
      }
    if(Box.AnchorType!=2) fprintf(cq->strip," \\end{center}");
    putc('\n',cq->strip); lines++;
    }
  else			//InputPS.sty
    {
    char RotFlag=0;
    if(Box.RotAngle!=0)
      {
      switch(Box.RotAngle)
        {
        case 90: RotFlag='l';break;
        case 180:RotFlag='u';break;
        case 270:RotFlag='r';break;
	}
      if(RotFlag)
        {
        if(Rotate>=false) Rotate=true;
                     else RotFlag=0;
	}
      if(!RotFlag)
        if(cq->err!=NULL && Verbosing>=0)
	  {
	  cq->perc.Hide();
	  fprintf(cq->err, _("\nError: InputPS.sty does not support box rotation. Use graphics.sty, graphicx.sty or epsfig.sty instead."));
	  }
      }
    fprintf(cq->strip,"\\begin{forcewidth}");
    switch(Box.HorizontalPos)
	{
	case 4:if(Box.Width>0)
		   {
		   fprintf(cq->strip,"{%2.2f\\textwidth}\n",Box.Width/100);break;
		   }
	case 3:fprintf(cq->strip,"{\\textwidth}\n");break;
	default:fprintf(cq->strip,"{%2.2fcm}\n",
                   (Box.Width>0) ? (Box.Width/10.0) : DEFAULT_BOX_WIDTH);
	}
    lines++;
    if(Box.AnchorType!=2) fprintf(cq->strip," \\begin{center}");
    if(RotFlag)
    {
      WP2LaTeXsty+=sty_rotate;
      fprintf(cq->strip," \\rotate[%c]{",RotFlag);
    }
    fprintf(cq->strip,"\\InputPS{\\FigDir/%s.eps}",NewFilename() );
    if(RotFlag) fprintf(cq->strip,"}");
    if(Box.AnchorType!=2) fprintf(cq->strip," \\end{center}");
    putc('\n',cq->strip); lines++;
    fprintf(cq->strip,"\\end{forcewidth}\n");lines++;
    }

  if(Box.CaptionSize>0 && Box.CaptionPos>0 && Box.AnchorType!=2)
    {
    fseek(cq->wpd,Box.CaptionPos,SEEK_SET);
    if(lines>0) NewLines(cq,lines,false);
    lines = 0;
    cq->Dispatch(DISP_DO_CAPTION, &Box.CaptionSize);
    }

  if(Box.AnchorType!=2)
	{
	BoxTexFoot(cq, Box);
	fprintf(cq->strip,"\n");
	lines++;
	}

  if(lines>0) NewLines(cq,lines,false);
  cq->char_on_line = (Box.AnchorType==2)?-1:false;
}


/*----------Converter for external standalone images----------*/

class TconvertedPass1_WPG: public TconvertedPass1
{
public:
    virtual int Convert_first_pass(void);
};


int TconvertedPass1_WPG::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_WPG() ");fflush(log);
#endif
DWORD fsize;
TBox Box;
SBYTE Bk_SaveWPG;

  DocumentStart = ftell(wpd);

  fsize = filesize(wpd);
  perc.Init(ftell(wpd), fsize, _("First pass WPG:") );

  ActualPos = 0;

  initBox(Box);
  Box.HorizontalPos=3;		/*3-Full */
  Box.Image_offset = ActualPos;
  Box.Image_size = fsize-Box.Image_offset;
  Box.Image_type=2;		/*WPG l2 parser has also autodetect for WPG l.1*/
  Box.Contents = 3; 		/*content image - every images are internally converted into WPG*/

  Bk_SaveWPG = SaveWPG;
  if(SaveWPG>=1) SaveWPG=0;
  ImageWP(this, wpd_filename(), Box);
  SaveWPG = Bk_SaveWPG;

  Finalise_Conversion(this);
  return(1);
}

TconvertedPass1 *Factory_WPG(void) {return new TconvertedPass1_WPG;}
FFormatTranslator FormatWPG("WPG",&Factory_WPG);


#ifndef NO_IMG
 FFormatTranslator FormatBMP("BMP",&Factory_WPG);
 FFormatTranslator FormatPNG("PNG",&Factory_WPG);
#endif

/*-------End of WPG converter--------*/
