/******************************************************************************
 * 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)
inline float DEG2RAD(const float x) {return((x)*(float)M_PI/180.0f);}

signed char CheckOversize = 1;


/// Maximal Bounding box is limited to 16322. Try to check for oversized image.
void CheckOversizedImage(Image *PImg)
{
FloatBBox bbx;
Image *CurrImg = PImg;

  InitBBox(bbx);
  while(CurrImg!=NULL)
  {
    if(!CurrImg->isEmpty())
      UpdateBBox(bbx, CurrImg->RotAngle, CurrImg->x,CurrImg->y,CurrImg->dx,CurrImg->dy);
    CurrImg = CurrImg->Next;
  }
  if(bbx.MaxX<bbx.MinX || bbx.MaxY<bbx.MinY) return;

  if(ceil(WPG2PS(fabs(bbx.MaxX)))>16322 || ceil(WPG2PS(fabs(bbx.MaxY)))>16322)
  {
    if(CheckOversize==0)
    {
      fprintf(stderr,_("\nWarning image is oversized %d %d, will produce error in LaTeX processing."),
            (int)ceil(WPG2PS(bbx.MaxX)), (int)ceil(WPG2PS(bbx.MaxY)));
      return;
    }

    vecResizeXY TRx(16322/ceil(WPG2PS(fabs((fabs(bbx.MaxX)>fabs(bbx.MaxY))?bbx.MaxX:bbx.MaxY))));
    CurrImg = PImg;
    while(CurrImg!=NULL)
    {
      if(CurrImg->VecImage!=NULL)
        CurrImg->VecImage->Transform(TRx);
      TRx.ApplyTransform(CurrImg->x, CurrImg->y);
      TRx.ApplyTransform(CurrImg->dx, CurrImg->dy);
      CurrImg = CurrImg->Next;
    }
  }
}


#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(CheckOversize>=0)
        CheckOversizedImage(&Img);
    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 image as EPS immediatelly, no multithread supported.
 inline void SavePictureEPS_MT(const string &FILE_NAME, Image &IMAGE_OBJ, FILE *ERR_STREAM, Percentor*PPERCENTOR) 
 {
  if(CheckOversize>=0)
      CheckOversizedImage(&IMAGE_OBJ);
  if(SavePictureEPS(FILE_NAME(),IMAGE_OBJ) < 0)
  {
    if(ERR_STREAM != NULL)
    {
      PPERCENTOR->Hide();
      fprintf(ERR_STREAM, _("\nError: Cannot save file: \"%s\"!"), FILE_NAME());
    }
  }
 }

#endif


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


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

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


typedef struct {
	uint32_t PS_unknown1;
	uint16_t PS_unknown2;
	uint16_t PS_unknown3;
       } WPGPSl1Record;


/** Store multibyte number. 3 nubmer formats are supported.
 Variant 0 - bytes from 0 to 0xFE; Variant 1 - words from 0 to 0x8000;
 Variant 3 - dwords. */
static void Wr_WP_DWORD(FILE *f, uint32_t 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 = (uint8_t)((d >> 8*2) & 0x00ffl);
  fwrite( &b, 1,1,f);
  b = (uint8_t)((d >> 8*3) & 0x00ffl) | 0x80;
  fwrite( &b, 1,1,f);
  b = (uint8_t)((d >> 8*0) & 0x00ffl);
  fwrite( &b, 1,1,f);
  b = (uint8_t)((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;
}


/** Detection procedure for WPG2 fragment without a header. */
int DetectFragmentWPG2(FILE *wpd, long FileSz)
{
long FilePos, CurrPos;
int CharNo;
WPG2Record Rec2;

  CurrPos = FilePos = ftell(wpd);
  if(FileSz < 0)
      FileSz = FileSize(wpd);
  
  CharNo = 0;
  while(!feof(wpd) && CurrPos<FileSz && CharNo<0x200)
     {
     Rec2.Class = fgetc(wpd);
     if(Rec2.Class>=10) {CharNo--;break;}		// Max known class is 9
     Rec2.Type = fgetc(wpd);
     if(Rec2.Type>=0x40 || Rec2.Type<=0) {CharNo--;break;} // Max known type is 0x3F
     Rd_WP_DWORD(wpd,&Rec2.Extension);
     Rd_WP_DWORD(wpd,&Rec2.RecordLength);

     if(Rec2.Type==1 && Rec2.RecordLength<0x15) {CharNo--;break;}

     CurrPos = ftell(wpd);
     CurrPos += Rec2.RecordLength;
     if(CurrPos > FileSz) {CharNo--;break;}		// Object offset overflows.
     fseek(wpd, CurrPos, SEEK_SET);

     CharNo++;
     }

  fseek(wpd,FilePos,SEEK_SET);
  return CharNo;
}


/** 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 = FILL_NONE;
  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;
  size_t 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(%dx%d) ",Width,Height);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 void NoMemory(TconvertedPass1 *cq, int Size)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#NoMemory(%d) ",Size);fflush(cq->log);
#endif
if (cq->err != NULL)
   {
   cq->perc.Hide();
   fprintf(cq->err, _("\nError: Not enough memory to store blob with %d bytes!"),Size);
   }
}


/*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);
}*/


/// @param[in]	WchI	Character encoded in internal encoding.
void AddCharacterToContainer(TextContainer *pTextCont, uint16_t WchI, const CpTranslator *PsNativeCP, const 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, const WPGTextL2 *pTextL2)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#Textl2_2PS() ");fflush(cq->log);
#endif
uint8_t TextColorIdx;
const RGB_Record BkTextColor = PSS.TextColor;
const float BkFontSize = PSS.FontSize;
uint16_t FontSize = 1100;		// Default font size
float MaxFontSizeWPGu = 0;
short LineCount = 1;
const uint16_t CodePageBk = OutCodePage;
TconvertedPass1 *cq5;
CpTranslator *PsNativeCP = GetTranslator("internalTOcp1276");
CpTranslator *PsNativeSym = GetTranslator("internalTOsymbol");
string StrObjType;

  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)
  {
    StrObjType.erase();
    //*cq5->ObjType=0;
    cq5->by = fgetc(cq5->wpd);
    if(feof(cq5->wpd)) break;

    if(cq5->by==0xC0)
    {
      uint16_t 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, cq5->ActualPos+1, 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)
    {
      fread(&cq5->subby, 1, 1, cq5->wpd);
      switch(cq5->subby)
      {
        case 0:			// Color RGB
		fseek(cq5->wpd, cq5->ActualPos+7, SEEK_SET);	// Offset ObjectStart+7
		PSS.TextColor.Red = fgetc(cq5->wpd);
		PSS.TextColor.Green = fgetc(cq5->wpd);
		PSS.TextColor.Blue = fgetc(cq5->wpd);
		break;
        case 1:{		// Font change
		  uint16_t FontSize2;
		  fseek(cq5->wpd, cq5->ActualPos+32, SEEK_SET);	// Offset ObjectStart+32
		  Rd_word(cq5->wpd, &FontSize2);			// 3600ths of inch
		  if(cq5->log !=NULL)
		  StrObjType.printf("Font Size %dWPu",FontSize2);

		  if(FontSize2!=FontSize)
		  {
		    FontSize = FontSize2;
		    PSS.FontSize =  mm2WPGu(inch2mm(FontSize/3600.0f));
		    if(MaxFontSizeWPGu <= PSS.FontSize) MaxFontSizeWPGu=PSS.FontSize;
		    PSS.FontSize = FontSize/7.5f;  //5.907599932798;	// Use factor to make mm after scalling :(
		  }
		}
		break;
        case 2: {		// Color Draw Perfect
		  fseek(cq5->wpd,2+1,SEEK_CUR);
		  TextColorIdx = fgetc(cq5->wpd);

		  RGB_Record NewColor = WPG_Palette[TextColorIdx];
		  if(cq5->log != NULL)
		      StrObjType.printf("Text Color IDX %u", (unsigned)TextColorIdx);
		  if(memcmp(&NewColor,&PSS.TextColor,sizeof(PSS.TextColor)))
		  {
		    PSS.TextColor = NewColor;
		  }
		}
		break;
      }
      fseek(cq5->wpd, cq5->ActualPos+1, SEEK_SET);	// rewind to subfunction
    }

    cq5->Dispatch(DISP_PROCESSKEY);
    if(!StrObjType.isEmpty() && cq5->log!=NULL)
        fprintf(cq5->log, "(%s)", StrObjType());
  }

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

 if(!pTextCont->isEmpty())
 {
   if(MaxFontSizeWPGu <= 0) MaxFontSizeWPGu=PSS.FontSize;	// Should be in [WPu]

   float y = pTextL2->LowLeftY;
   float dy = pTextL2->UpRightY - pTextL2->LowLeftY;
   if(LineCount >1)
   {
     dy /= LineCount;
     y += (LineCount-1) * dy;
   }

   pTextCont->PosX = pTextL2->LowLeftX;
   pTextCont->PosY = y;
   pTextCont->PosY += dy/2;			// Horizontal center of the area.
   pTextCont->PosY -= MaxFontSizeWPGu / 2;
   pTextCont->PosY += 0.15*MaxFontSizeWPGu;	// Shift text slightly up
   pTextCont->FontOrientation = pTextL2->RotAngle;
   pTextCont->RotCenterX = (pTextL2->UpRightX - pTextL2->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, 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;
uint16_t FontSize = 1100;
short LineCount = 1;
const uint16_t CodePageBk = OutCodePage;
RGB_Record BkTextColor = PSS.TextColor;
TconvertedPass1 *cq6;				// Converter for embedded text fragment
long ActualPos;
CpTranslator *PsNativeCP = GetTranslator("internalTOcp1276");
CpTranslator *PsNativeSym = GetTranslator("internalTOsymbol");
string StrObjType;

  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;
    StrObjType.erase();
    cq6->by = fgetc(cq6->wpd);

    if(cq6->by==0xD3 || cq6->by==0xD4 || cq6->by==0xE1)
      {
      ActualPos = ftell(cq6->wpd);
      cq6->subby = fgetc(cq6->wpd);

      if(cq6->by==0xD3)
        {
        if(cq6->subby==5)
	  {
	  //CrackObject(cq6, ActualPos+0x10);
	  uint8_t b;
	  fseek(cq->wpd, 5L, SEEK_CUR);		// Skip [size]<flags>[size non deletable]
	  fread(&b, 1, 1, cq->wpd);		/* 0-Left, 3-Right, 2-Center, 1-Full */
	  switch(b)
	    {
	    case 0: pTxC->TextAlign&=~6; break;	// environment = left
	    case 2: pTxC->TextAlign|=6; break;	// environment = center
	    case 3: pTxC->TextAlign &= ~6;
		    pTxC->TextAlign |= 2;
		    break;
	    }
          if(cq6->log != NULL)
	      StrObjType.printf("Text align %u", (unsigned)b);
	  }
        }

      if(cq6->by==0xD4)
	{
	if(cq6->subby==0x1B)
	  {
	  fseek(cq6->wpd,6+2,SEEK_CUR); //<flags><PIDsNo>[descriptor][NDelSize]
	  Rd_word(cq6->wpd, &FontSize);
		// @TODO This should be cleaned up.
	  PSS.FontSize = FontSize/5.907599932798; // Will be scalled to from WPGU to PSU, use factor to make mm after scalling.
	  if(cq6->log != NULL)
	    StrObjType.printf("Text size %fmm", WPGu2mm(FontSize));
	  }
	}

      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
	  if(cq6->log != NULL)
	    StrObjType.printf("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);
      uint16_t 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 uint16_t 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(!StrObjType.isEmpty() && cq6->log!=NULL)
      fprintf(cq6->log, "(%s)", StrObjType());
    }
  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;
    if(fabs(dy)>0.01)
    {
      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->RotCenterY = dy / 2;
    switch(pTxC->TextAlign&6)
    {
      case 2: pTxC->RotCenterX = dx / -2;
              pTxC->PosX += dx;
	      break;
      case 6: pTxC->RotCenterX = 0;
              pTxC->PosX += dx / 2;
	      break;
      default:
	      pTxC->RotCenterX = dx / 2;
	      break;
    }
    //VectList.AddObject(pTextCont);
  }
 
  return pTxC;
}


/** Load array of points word packed.
 * @param[in]	Precision	true 32bit; false 16bit */
float *LoadPoints(TconvertedPass1 *cq, int n, bool Precision, float *Points)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#LoadPoints(%d) ",n);fflush(cq->log);
#endif
float *p;

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

//  if(LineWidth<=0) LineWidth=0;
//              else LineWidth /= 2;

  float x, y;
  while(n-->0)
    {
      if(!Precision)
        {
        int16_t w;
	Rd_word(cq->wpd, (uint16_t *)&w);
	x = w;
	Rd_word(cq->wpd,(uint16_t *)&w);
	y = w;
        }
      else
        {
        int32_t d;
	Rd_dword(cq->wpd, (uint32_t*)&d);
	x = d / 65536.0;
	Rd_dword(cq->wpd,(uint32_t*)&d);
	y = d / 65536.0;
        }

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

      if(x-LineWidth<bbx.MinX) bbx.MinX=x-LineWidth;
      if(x+LineWidth>bbx.MaxX) bbx.MaxX=x+LineWidth;
      if(y-LineWidth<bbx.MinY) bbx.MinY=y-LineWidth;
      if(y+LineWidth>bbx.MaxY) bbx.MaxY=y+LineWidth;
*/
      *p++ = x;
      *p++ = y;
      }
#ifdef DEBUG
  fprintf(cq->log,"\n#~LoadPoints(%d) ",n);fflush(cq->log);
#endif
  return(Points);
}


void FixBoundingBox(float *Points, int n, FloatBBox &bbx, AbstractTransformXY *TRx, float LineWidth)
{
  if(Points==NULL) return;

  if(LineWidth<=0) LineWidth=0;
              else LineWidth /= 2;

  while(n-->0)
  {
    if(TRx) TRx->ApplyTransform(Points[0],Points[1]);

    if(Points[0]-LineWidth<bbx.MinX) bbx.MinX=Points[0]-LineWidth;
    if(Points[0]+LineWidth>bbx.MaxX) bbx.MaxX=Points[0]+LineWidth;

    if(Points[1]-LineWidth<bbx.MinY) bbx.MinY=Points[1]-LineWidth;
    if(Points[1]+LineWidth>bbx.MaxY) bbx.MaxY=Points[1]+LineWidth;

    Points+=2;
  }
}


/** Check capacity of object for a given amount of polygons. */
static void FixPolySize(TconvertedPass1 *cq, uint16_t *P_size, uint32_t ObjSize)
{
  if(ObjSize < 2+2*(uint32_t)*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, int32_t CenterX, int32_t CenterY)
{
 vecTransform *Tx = new vecTransform;
 Tx->CenterX = CenterX;
 Tx->CenterY = CenterY;
 Tx->RotAngle = TRx.RotAngle;
 if(TRx.Flags & WPG2_Transform::WPG2_TRN)
   {
   Tx->TranslateX = TRx.TranslateX;
   Tx->TranslateY = 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 Val = 
     ScaleX*x + SkewX*y  + TranslateX;
 y = SkewY*x  + y*ScaleY + TranslateY;
 x = Val;
}


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


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

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

  Raster2DAbstract **getBrushPtr(uint16_t index);
  void ClearBrushes(void);

};

class TWPG2ConvStatus: public TImgConvStatus
{
public:
  uint8_t VTextAlign;

  TWPG2ConvStatus(void): TImgConvStatus() {VTextAlign=0;}
};


Raster2DAbstract **TImgConvStatus::getBrushPtr(uint16_t index)
{
if(index>255 || index<32) return NULL;
  index -= 32;
  if(BrushList==NULL)
  {
    BrushList = (Raster2DAbstract**)calloc(index+1, sizeof(Raster2DAbstract*));
    if(BrushList==NULL) return NULL;
    return &BrushList[index];
  }

  if(index>=MaxBrushes)
  {
   Raster2DAbstract **BrushList2 = (Raster2DAbstract**)realloc(BrushList, (index+1)*sizeof(Raster2DAbstract*));
   if(BrushList2==NULL) return NULL;
   memset(&BrushList2[MaxBrushes], 0, (index+1)-MaxBrushes);
   BrushList = BrushList2;
   MaxBrushes = index+1;
  }
  return &BrushList[index];
}


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


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


void FixLineStyle(const unsigned Flags, vecPen *pPen)
{
  if((Flags & WPG2_Transform::WPG2_FRM) == 0)
      pPen->LineStyle=0;	// Disable painting object frame.
  else
      {if(pPen->LineStyle==0) pPen->LineStyle=1;}
}


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

  TRx.Precision = StartWPG.PosSizePrecision;
  switch(Rec2.Type)
    {
    case 0x1: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);
		}
		  // Start extensions must be read separatelly.
	     cq->recursion++;
             while(cq->ActualPos<ResourceEnd && Rec2.Extension-->0)  // Consider all extensions to be nested.
	       {	       
	       WPG2Record Rec2b;
               if(NewObjectPos!=ftell(cq->wpd))
	           fseek(cq->wpd,NewObjectPos,SEEK_SET);
               cq->ActualPos = NewObjectPos;

	       Rec2b.Class = fgetc(cq->wpd);
	       Rec2b.Type = fgetc(cq->wpd);		
	       
	       Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	       Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	       cq->ActualPos = ftell(cq->wpd);
	       NewObjectPos = cq->ActualPos + Rec2b.RecordLength;
	       
               ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos, ResourceEnd, /*CurrImg,*/
				  ImgSts, bbx, VectList);
	       if(Rec2b.Type==0x31 || Rec2b.Type==0x32)
		 {
	         memcpy(&PSS.PaperBackground, &PSS.FillColor, sizeof(PSS.PaperBackground));
		 memset(&PSS.FillColor, 0, sizeof(PSS.FillColor));			// Revert initial value.
		 }
	       if(Rec2b.Type==0x33 || Rec2b.Type==0x34)
		 {
	         memset(&PSS.FillBackground, 0xFF, sizeof(PSS.FillBackground));
		 }
	       }
	     cq->recursion--;
	     strcpy(cq->ObjType,"Start WPG2");
	     break;
    case 0x2:strcpy(cq->ObjType,"End WPG");
		 NewObjectPos = 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: {
              uint16_t PatternIndex;
              RdWORD_LoEnd(&PatternIndex,cq->wpd);              
              uint16_t NumberOfResolutions;
              RdWORD_LoEnd(&NumberOfResolutions,cq->wpd);

              Raster2DAbstract **BrushPtr = (NumberOfResolutions==0) ? NULL : ImgSts.getBrushPtr(PatternIndex);
              if(BrushPtr==NULL)
              {
                if(NumberOfResolutions > 0)
                {
		  cq->perc.Hide();
		  fprintf(cq->err, _("\nError: Cannot allocate slot for brush #%u."), (unsigned)PatternIndex);                
                }
	      }
              else
              {
                cq->recursion++;
                while(cq->ActualPos<ResourceEnd && Rec2.Extension-->0)  // Consider all extensions to be nested.
	        {	       
	          WPG2Record Rec2b;
                  if(NewObjectPos!=ftell(cq->wpd))
	              fseek(cq->wpd,NewObjectPos,SEEK_SET);
                  cq->ActualPos = NewObjectPos;

	          Rec2b.Class = fgetc(cq->wpd);
	          Rec2b.Type = fgetc(cq->wpd);		
	       
	          Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	          Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	          cq->ActualPos = ftell(cq->wpd);
	          NewObjectPos = cq->ActualPos + Rec2b.RecordLength;
	       
                  ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos, ResourceEnd, /*CurrImg,*/
				  ImgSts, bbx, VectList);

                }
                cq->recursion--;
	                  
			// look to the next object for Bitmap.
                //ImgSts.UpdateBrush(PatternIndex);
              }
              snprintf(cq->ObjType, sizeof(cq->ObjType),"!Pattern definition #%d", PatternIndex);
              break;
              }
    case 0xA:strcpy(cq->ObjType,"!Comment"); break;
    case 0x0B:strcpy(cq->ObjType,"!Color Transfer"); break;
    case 0x0C:{
	      WPG_records WPG;
	      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)->setR(i,fgetc(cq->wpd));
		    (*Palette)->setG(i,fgetc(cq->wpd));
		    (*Palette)->setB(i,fgetc(cq->wpd));
		    fgetc(cq->wpd);			//Opacity?
		    }
		}
              }
	      break;
    case 0x0D:{
	      WPG_records WPG;
	      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++)
		    {
                    uint16_t num;
                    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->setR(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->setG(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	(*Palette)->setB(i,num);
		    RdWORD_LoEnd(&num,cq->wpd);	//Opacity?
		    }
		}
              }
              break;
    case 0x0E:{
	      WPG_records WPG;
	      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;
                 }

	       VectorRaster *vr;
               if(ImgSts.dy>0) 
                   vr = new VectorRaster(ImgSts.x+ImgSts.dx/2, ImgSts.y+ImgSts.dy/2, ImgSts.dx, ImgSts.dy, DEG2RAD(ImgSts.RotAngle));
               else 
                 {
		 Flip2D(Raster);
		 vr = new VectorRaster(ImgSts.x+ImgSts.dx/2, ImgSts.y-ImgSts.dy/2, ImgSts.dx, ImgSts.dy, DEG2RAD(ImgSts.RotAngle));
                 }
	       vr->AttachRaster(Raster);
	       vr->AttachPalette(*Palette);               
	       UpdateBBox(bbx, ImgSts.RotAngle, ImgSts.x, ImgSts.y, ImgSts.dx, fabs(ImgSts.dy));  // Calculate bbx in [WPU].
               VectList.AddObject(vr);

	      /* VectorRectangle *vrr;
		vrr = new VectorRectangle(ImgSts.y+fabs(ImgSts.dy), ImgSts.y, ImgSts.x, ImgSts.x+ImgSts.dx);
	        //vrr = new VectorRectangle(ImgSts.y+50, ImgSts.y, ImgSts.x, ImgSts.x+50);
		vrr->BrushStyle = 0;
		VectList.AddObject(vrr);
		vrr = new VectorRectangle(vr->RightCenterY+50, vr->RightCenterY, vr->RightCenterX, vr->RightCenterX+50);
		vrr->BrushStyle = 0;
		VectList.AddObject(vrr); */
	       } 
	       break;

    case 0x0F:{//CrackObject(cq,NewObject);
	      TextContainer *TxC = TextWPG2_2PS(cq,Rec2.RecordLength,PSS,ImgSts.x,ImgSts.y,ImgSts.dx,ImgSts.dy,ImgSts.RotAngle);
	      if(TxC!=NULL)
	        {
	        if(TxC->isEmpty())
		    delete TxC;
		else
		  {
		  if(ImgSts.dx<1e-6)
		  {
		    TxC->CalculateExtent(ImgSts.dx,ImgSts.dy);
		    ImgSts.dx = mm2WPGu(ImgSts.dx);
		    ImgSts.dy = mm2WPGu(ImgSts.dy) / 1.3;
		    if(ImgSts.VTextAlign == 0)
		      TxC->PosY -= 0.8*mm2WPGu(PSS.FontSize);		// Shift text paint to the line bottom.
		    UpdateBBox(bbx, ImgSts.RotAngle, ImgSts.x, ImgSts.y, ImgSts.dx, ImgSts.dy);  // Calculate bbx in [WPU].
		  }
	          else
                  {
		    if(fabs(ImgSts.dx)<0.1) ImgSts.dx=0.5f*mm2WPGu(PSS.FontSize);
		    if(fabs(ImgSts.dy)<0.1) ImgSts.dy=mm2WPGu(PSS.FontSize);
		    UpdateBBox(bbx, ImgSts.RotAngle, ImgSts.x, ImgSts.y-0.2f*ImgSts.dy, ImgSts.dx, 1.3f*ImgSts.dy);  // Calculate bbx in [WPU].
		  }
		  VectList.AddObject(TxC);
	          }
		}

#ifdef _DEBUG
/*			// Debug purpose rectangle	      
              VectorRectangle *Vr = new VectorRectangle(ImgSts.y, ImgSts.y+mm2WPGu(PSS.FontSize),
					   ImgSts.x, ImgSts.x+ImgSts.dx);
	      PSS.FillPattern = FILL_NONE;
	      Vr->AttribFromPSS(PSS);
	      VectList.AddObject(Vr);

	      Vr = new VectorRectangle(ImgSts.y, ImgSts.y+mm2WPGu(3),
					   ImgSts.x, ImgSts.x+mm2WPGu(3));
	      PSS.FillPattern = FILL_NONE;
	      Vr->AttribFromPSS(PSS);
	      VectList.AddObject(Vr); */
#endif

	      strcpy(cq->ObjType,"Text Data");
	      break;
	      }
    case 0x10:strcpy(cq->ObjType,"!Chart Style");break;
    case 0x11:strcpy(cq->ObjType,"Chart Data");break;
    case 0x12:{				// PostScript or another format inside WPG2.
		//CrackObject(cq,NewObjectPos);//!!!!
		uint16_t AccessoryDataLen;
		Rd_word(cq->wpd,&AccessoryDataLen);
		if(Rec2.RecordLength>0x12)
                  {
		  if(AccessoryDataLen>0)
		      fseek(cq->wpd, AccessoryDataLen, SEEK_CUR);
		  if(DetectFragmentWPG2(cq->wpd,NewObjectPos) >= 1)
                    {
		    cq->recursion++;
		    cq->ActualPos = ftell(cq->wpd);
		    while(cq->ActualPos < NewObjectPos)
		      {
		      WPG2Record Rec2b;
	              Rec2b.Class = fgetc(cq->wpd);
	              Rec2b.Type = fgetc(cq->wpd);
	              Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	              Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	              cq->ActualPos = ftell(cq->wpd);

		      uint32_t NewObjectPos2 = cq->ActualPos + Rec2b.RecordLength;
                      ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos2, NewObjectPos, /*CurrImg,*/
				  ImgSts, bbx, VectList);

		      if(NewObjectPos2 != ftell(cq->wpd))
	                  fseek(cq->wpd,NewObjectPos2,SEEK_SET);
		      cq->ActualPos = NewObjectPos2;
		      }
		    cq->recursion--;
	            }
                    else
		      {
		      if(EmImg!=NULL)
		        {
		        EmbeddedImg *Ei2 = (EmbeddedImg *)malloc(sizeof(EmbeddedImg));
			if(Ei2!=NULL)
                          {
		          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;
                          }
			else NoMemory(cq,sizeof(EmbeddedImg));
                        }
		      }
                    }
		  strcpy(cq->ObjType,"Object Image");
		  break;
		  }
    case 0x15:{strcpy(cq->ObjType,"Polyline");
		  //  CrackObject(cq,NewObjectPos);
		  float *Points;
		  WPG_records WPG;

		  TRx.LoadWPG2Flags(cq->wpd);

		  Rd_word(cq->wpd,&WPG.Curve.Count);
		  //if(StartWPG.PosSizePrecision==1) fseek(cq->wpd,2,SEEK_CUR);

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

		  if(PSS.FillTransparency<0xFF && (TRx.Flags & WPG2_Transform::WPG2_FILL)!=0)	//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;
		      FixLineStyle(TRx.Flags, pVecPoly);
		      if(pVecPoly->BrushStyle == (0x80|FILL_NONE))	// Filled with linecolor.
                      {
                        memcpy(&pVecPoly->FillBackground,&pVecPoly->LineColor,sizeof(RGB_Record));
			pVecPoly->BrushStyle = 0x80|FILL_SOLID;
		      }
                      //pVecPoly->Transform(vecResizeXY(WPGu2PSu(1)));	// Scale contents from WPGu to PSu.
                      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)));	// Scale contents from WPGu to PSu.
                      VectList.AddObject(pVecLine);
                      }
		  break;
		  }
    case 0x16:cq->PleaseReport("Polyspline","WPG"); strcpy(cq->ObjType,"!Polyspline"); break;
    case 0x17:{strcpy(cq->ObjType,"Polycurve");
		  float *Points;
		  WPG_records WPG;

		  TRx.LoadWPG2Flags(cq->wpd);
			//!!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, StartWPG.PosSizePrecision>0);
		  if(Points==NULL) break;
                  FixBoundingBox(Points, WPG.Curve.Count, bbx);
                  VectorCurve *pVecCurve = new VectorCurve(Points, WPG.Curve.Count);
                  pVecCurve->AttribFromPSS(PSS);
                  //pVecCurve->Transform(vecResizeXY(WPGu2PSu(1)));			// Scale contents from WPGu to PSu.
		  if(0xFF==PSS.FillTransparency || 0!=(TRx.Flags&WPG2_Transform::WPG2_FILL))
		      pVecCurve->BrushStyle = FILL_NONE;
		  FixLineStyle(TRx.Flags, pVecCurve);
		  VectList.AddObject(pVecCurve);
		  break;
		  }
    case 0x18:{   WPG_records WPG;
	          strcpy(cq->ObjType,"Rectangle");
		  //CrackObject(cq,NewObjectPos);
		  TRx.LoadWPG2Flags(cq->wpd);

		  LoadWPG2Rectangle(cq->wpd,WPG._2Rect);
		  
		  float X, Y;
		  const float LineWidthD2 = PSu2WPGu(PSS.LineWidth) / 2.0f;
		  for(int i=0; i<4; i++)
		  {
		    switch(i)
		    {
		      case 0: X=WPG._2Rect.X_ur; Y=WPG._2Rect.Y_ur; 
			      break;		      
		      case 1: X=WPG._2Rect.X_ll; Y=WPG._2Rect.Y_ur;
			      break;
		      case 2: X=WPG._2Rect.X_ur; Y=WPG._2Rect.Y_ll;
			      break;		      
		      case 3: X=WPG._2Rect.X_ur; Y=WPG._2Rect.Y_ll;
			      break;
		    }		    
		    TRx.ApplyTransform(X,Y);
		    UpdateBBox(bbx, 0, X-LineWidthD2,Y-LineWidthD2, 2*LineWidthD2,2*LineWidthD2);
		  }
		  VectorRectangleArc *pRectArc = new VectorRectangleArc(
                        WPG._2Rect.Y_ll,WPG._2Rect.Y_ur, WPG._2Rect.X_ur, WPG._2Rect.X_ll,
                        WPG._2Rect.H_radius, WPG._2Rect.V_radius);
		  if(fabs(TRx.SkewX)<0.1 && fabs(TRx.SkewY)<0.1 && fabs(TRx.RotAngle)<0.5)
		      pRectArc->Transform(TRx);
		  else
		      pRectArc->Tx = new vecResizeCTM(TRx);

		  pRectArc->AttribFromPSS(PSS);
		  if(PSS.FillTransparency>=0xFF || (TRx.Flags & WPG2_Transform::WPG2_FILL)==0)	//check Fill flag
		      pRectArc->BrushStyle = FILL_NONE;
		  FixLineStyle(TRx.Flags,pRectArc);		  
		  VectList.AddObject(pRectArc);
		  break;
		  }
    case 0x19:{
		  WPG_records WPG;
	          strcpy(cq->ObjType,"Arc");
                  //CrackObject(cq,NewObject);
		  TRx.LoadWPG2Flags(cq->wpd);
		  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(PSS.FillTransparency>=0xFF || (TRx.Flags & WPG2_Transform::WPG2_FILL)==0)
		      pVecEllipse->BrushStyle=FILL_NONE;	// Disable object fill.
                  FixLineStyle(TRx.Flags, pVecEllipse);
		  //if(b) {pVecEllipse->FillColor.Red=0x80;b=false;}
                  VectList.AddObject(pVecEllipse);
		  break;
		  }

    case 0x1A: {		// Works like group, but all rendering settings should be ignorred.
	      FloatBBox bbxIn;
	      bbxIn.MinX=65537; bbxIn.MaxX=-1; bbxIn.MinY=65537; bbxIn.MaxY=-1;
	      TRx.LoadWPG2Flags(cq->wpd);
	      VectorList VectListIn;
	      cq->recursion++;
              while(cq->ActualPos<ResourceEnd && Rec2.Extension-->0)  // Consider all extensions to be nested.
	        {
	        WPG2Record Rec2b;
                if(NewObjectPos!=ftell(cq->wpd))
	            fseek(cq->wpd,NewObjectPos,SEEK_SET);
                cq->ActualPos = NewObjectPos;

	        Rec2b.Class = fgetc(cq->wpd);
	        Rec2b.Type = fgetc(cq->wpd);

	        Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	        Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	        cq->ActualPos = ftell(cq->wpd);
	        NewObjectPos = cq->ActualPos + Rec2b.RecordLength;

                ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos, ResourceEnd, /*CurrImg,*/
				  ImgSts, bbxIn, VectListIn);
	        }
	      cq->recursion--;

	      //{FILE *F = fopen("C:\\temp\\29\\group1.txt","wb");VectListIn.Dump2File(F);fclose(F);}

	      if(VectListIn.VectorObjects > 0)
                {		// Bounding box is in PS units.
	        TRx.ApplyTransform(bbxIn.MinX,bbxIn.MinY);
	        UpdateBBox(bbx,0, bbxIn.MinX,bbxIn.MinY,0,0);
	        TRx.ApplyTransform(bbxIn.MaxX,bbxIn.MaxY);
	        UpdateBBox(bbx,0, bbxIn.MaxX,bbxIn.MaxY,0,0);

		//VectListIn.Transform(vecResizeXY(1/WPGu2PSu(1)));  // Return units to WPGu.	        
		//TRx.ScaleX *= WPGu2PSu(1);			// Transform WPu to PSu
		//TRx.ScaleY *= WPGu2PSu(1);
		//TRx.SkewX *= WPGu2PSu(1);
		//TRx.SkewY *= WPGu2PSu(1);
		//TRx.TranslateX *= WPGu2PSu(1);
		//TRx.TranslateY *= WPGu2PSu(1);
	        VectListIn.Transform(TRx);

		//{FILE *F = fopen("C:\\temp\\29\\group2.txt","wb");VectListIn.Dump2File(F);fclose(F);}

	        VectList.Append(VectListIn);
                }
	      strcpy(cq->ObjType,"Compound Polygon");
	      break;
	      }
    case 0x1B:{
	      WPG_records WPG;
	      strcpy(cq->ObjType,"Bitmap position");
	      //CrackObject(cq,NewObject);//!!!!
	      TRx.LoadWPG2Flags(cq->wpd);
              //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;
		 }
	      else 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;
		 }
              else break;
              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;
	      break;
	      }
    case 0x1C:{
              WPG_records WPG;
	      strcpy(cq->ObjType,"Text Line");
	      TRx.LoadWPG2Flags(cq->wpd);
	      ImgSts.RotAngle = TRx.RotAngle;

	      LoadWPG2TextLine(cq->wpd,WPG._2TxtLine);
	      ImgSts.x = WPG._2TxtLine.Xref;
	      ImgSts.y = WPG._2TxtLine.Yref;
	      ImgSts.dx = ImgSts.dy = 0;
	      ImgSts.VTextAlign = WPG._2TxtLine.VAlign;
	      TRx.RotAngle = 0;
	      TRx.ApplyTransform(ImgSts.x,ImgSts.y);
	      UpdateBBox(bbx, 0, ImgSts.x, ImgSts.y, 0, 0);
	      break;
	      }
    case 0x1D:{
	      WPG_records WPG;
	      strcpy(cq->ObjType,"Text Block");
              TRx.LoadWPG2Flags(cq->wpd);
	      ImgSts.RotAngle = TRx.RotAngle;

	      float Sx, Sy;
              float RightX, RightY;
              float TopX, TopY;

              if(StartWPG.PosSizePrecision==0)
                {
                LoadWPG2TextBlock(cq->wpd, WPG._2TxtBlock);
		Sx = (WPG._2TxtBlock.LowLeftX + WPG._2TxtBlock.UpRightX) / 2;
                Sy = (WPG._2TxtBlock.LowLeftY + WPG._2TxtBlock.UpRightY) / 2;
                RightX = WPG._2TxtBlock.UpRightX;
                TopY = WPG._2TxtBlock.UpRightY;
                }
              if(StartWPG.PosSizePrecision==1)
                {
		LoadWPG2TextBlockDbl(cq->wpd, WPG._2TxtBlockDbl);
		Sx = (WPG._2TxtBlockDbl.LowLeftX + WPG._2TxtBlockDbl.UpRightX) / (float)0x20000;
                Sy = (WPG._2TxtBlockDbl.LowLeftY + WPG._2TxtBlockDbl.UpRightY) / (float)0x20000;
                RightX = WPG._2TxtBlockDbl.UpRightX / (float)0x10000;
                TopY = WPG._2TxtBlockDbl.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;

              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:{
	      FloatBBox bbxIn;
	      bbxIn.MinX=65537; bbxIn.MaxX=-1; bbxIn.MinY=65537; bbxIn.MaxY=-1;
	      TRx.LoadWPG2Flags(cq->wpd);
	      VectorList VectListIn;
	      cq->recursion++;
              while(cq->ActualPos<ResourceEnd && Rec2.Extension-->0)  // Consider all extensions to be nested.
	        {
	        WPG2Record Rec2b;
                if(NewObjectPos!=ftell(cq->wpd))
	            fseek(cq->wpd,NewObjectPos,SEEK_SET);
                cq->ActualPos = NewObjectPos;

	        Rec2b.Class = fgetc(cq->wpd);
	        Rec2b.Type = fgetc(cq->wpd);
	       
	        Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	        Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	        cq->ActualPos = ftell(cq->wpd);
	        NewObjectPos = cq->ActualPos + Rec2b.RecordLength;
	       
                ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos, ResourceEnd, /*CurrImg,*/
				  ImgSts, bbxIn, VectListIn);
	        }
	      cq->recursion--;

	      //{FILE *F = fopen("C:\\temp\\29\\group1.txt","wb");VectListIn.Dump2File(F);fclose(F);}

	      if(VectListIn.VectorObjects > 0)
                {		// Bounding box is in PS units.
	        TRx.ApplyTransform(bbxIn.MinX,bbxIn.MinY);
	        UpdateBBox(bbx,0, bbxIn.MinX,bbxIn.MinY,0,0);
	        TRx.ApplyTransform(bbxIn.MaxX,bbxIn.MaxY);
	        UpdateBBox(bbx,0, bbxIn.MaxX,bbxIn.MaxY,0,0);

		//VectListIn.Transform(vecResizeXY(1/WPGu2PSu(1)));  // Return units to WPGu.	        
		//TRx.ScaleX *= WPGu2PSu(1);			// Transform WPu to PSu
		//TRx.ScaleY *= WPGu2PSu(1);
		//TRx.SkewX *= WPGu2PSu(1);
		//TRx.SkewY *= WPGu2PSu(1);
		//TRx.TranslateX *= WPGu2PSu(1);
		//TRx.TranslateY *= WPGu2PSu(1);
	        VectListIn.Transform(TRx);

		//{FILE *F = fopen("C:\\temp\\29\\group2.txt","wb");VectListIn.Dump2File(F);fclose(F);}

	        VectList.Append(VectListIn);
                }	      
	      strcpy(cq->ObjType,"Group");
	      break;
	      }
    case 0x21:{	      
	      FloatBBox bbxIn;
	      bbxIn.MinX=65537; bbxIn.MaxX=-1; bbxIn.MinY=65537; bbxIn.MaxY=-1;
	      TRx.LoadWPG2Flags(cq->wpd);
	      VectorList VectListIn;
	      cq->recursion++;
              while(cq->ActualPos<ResourceEnd && Rec2.Extension-->0)  // Consider all extensions to be nested.
	        {
	        WPG2Record Rec2b;
                if(NewObjectPos!=ftell(cq->wpd))
	            fseek(cq->wpd,NewObjectPos,SEEK_SET);
                cq->ActualPos = NewObjectPos;

	        Rec2b.Class = fgetc(cq->wpd);
	        Rec2b.Type = fgetc(cq->wpd);

	        Rd_WP_DWORD(cq->wpd,&Rec2b.Extension);
	        Rd_WP_DWORD(cq->wpd,&Rec2b.RecordLength);
	        cq->ActualPos = ftell(cq->wpd);
	        NewObjectPos = cq->ActualPos + Rec2b.RecordLength;
	       
                ProcessWPG2Token(cq, Rec2b, StartWPG, Img, Palette, PSS, EmImg, NewObjectPos, ResourceEnd, /*CurrImg,*/
				  ImgSts, bbxIn, VectListIn);
	        }
	      cq->recursion--;
	      if(VectListIn.VectorObjects > 0)
                {
	        TRx.ApplyTransform(bbxIn.MinX,bbxIn.MinY);
	        UpdateBBox(bbx,0, bbxIn.MinX,bbxIn.MinY,0,0);
	        TRx.ApplyTransform(bbxIn.MaxX,bbxIn.MaxY);
	        UpdateBBox(bbx,0, bbxIn.MaxX,bbxIn.MaxY,0,0);
	        //TRx.TranslateX *= WPGu2PSu(1);
	        //TRx.TranslateY *= WPGu2PSu(1);
	        VectListIn.Transform(TRx);
	        VectList.Append(VectListIn);
                }
	      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:{
	      RGB_Record Color;
	      Color.Red = fgetc(cq->wpd);
	      Color.Green = fgetc(cq->wpd);
	      Color.Blue = fgetc(cq->wpd);	
	      snprintf(cq->ObjType, sizeof(cq->ObjType),"!Pen Back Color %2.2X %2.2X %2.2X", Color.Red, Color.Green, Color.Blue);
	      break;
	      }
    case 0x28:strcpy(cq->ObjType,"!DP Pen Back Color");break;
    case 0x29:{uint8_t PenStyle;
	      static const uint8_t ConvertPen[16]={1,	// 0-solid line
				1,2,5,7,10,3,19/*11*/,4,6,8,12,13,16,17/*14*/,18/*15*/};
	      PenStyle = fgetc(cq->wpd);
	      snprintf(cq->ObjType, sizeof(cq->ObjType),"Pen Style %u",PenStyle);
	      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:{
	      uint16_t Width, Height;
	      float LineWidth;

	      Rd_word(cq->wpd,(uint16_t*)&Width);
	      Rd_word(cq->wpd,(uint16_t*)&Height);
	      LineWidth = WPGu2PSu(Width>Height?Width:Height);
	      if(PSS.LineWidth!=LineWidth)
		{
		PSS.LineWidth = LineWidth;
		PSS.dirty |= PSS_LineWidth;
		}
              snprintf(cq->ObjType, sizeof(cq->ObjType),"Pen Size %d", Width>Height?Width:Height);
	      break;
	      }
    case 0x2C:strcpy(cq->ObjType,"!DP Pen Size");break;
    case 0x2D:{
	      uint8_t StartCap = fgetc(cq->wpd);
	      uint8_t EndCap = fgetc(cq->wpd);
	      if(StartCap==EndCap && StartCap<=2)
	        {
		PSS.LineCap = StartCap;
		snprintf(cq->ObjType, sizeof(cq->ObjType),"Line Cap %u",StartCap);
		break;
	        }
	      strcpy(cq->ObjType,"!Line Cap");
	      break;
	      }
    case 0x2E:{
	      uint8_t LineJoin = fgetc(cq->wpd);
	      switch(LineJoin)
	        {
		case 1: PSS.LineJoin=2;break;
		case 2: PSS.LineJoin=0;break;
		case 3: PSS.LineJoin=1;break;
		}
              snprintf(cq->ObjType, sizeof(cq->ObjType),"%sLine Join %u", (LineJoin>3||LineJoin<=0)?"!":"", LineJoin);
	      }
	      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);
	      uint16_t 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);
		 }*/

		snprintf(cq->ObjType, sizeof(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);
		PSS.FirstTimeFix = false;
		}
                else
                  strcpy(cq->ObjType,"!Brush Fore Color");
	      break;
	      }
    case 0x32:{
              uint16_t R,G,B,A;
              char GradientType;
	      GradientType = fgetc(cq->wpd);
	      uint16_t 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;
                snprintf(cq->ObjType, sizeof(cq->ObjType),"DP Brush Fore Color n:%d (%2X %2X %2X; %2X)",
		    (int)BrushColors, R, G, B, A);
		PSS.FirstTimeFix = false;
                }
              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);
	      ImgSts.BkTransparency = fgetc(cq->wpd);
	      if(ImgSts.BkTransparency==0) PSS.FillPattern|=0x80;
			              else PSS.FillPattern&=~0x80;
	      snprintf(cq->ObjType, sizeof(cq->ObjType),"Brush Back Color (%2.2X %2.2X %2.2X; %2X)",
		  PSS.FillBackground.Red, PSS.FillBackground.Green, PSS.FillBackground.Blue, ImgSts.BkTransparency);
	      //PSS.FillBackground.Green = 128;
	      break;
	      }
    case 0x34:{
	      uint16_t R,G,B,A;
              Rd_word(cq->wpd,&R);
              Rd_word(cq->wpd,&G);
              Rd_word(cq->wpd,&B);
	      Rd_word(cq->wpd,&A);
              PSS.FillBackground.Red = R >> 8;
	      PSS.FillBackground.Green = G >> 8;
	      PSS.FillBackground.Blue = B >> 8;
	      ImgSts.BkTransparency = A >> 8;
	      if(ImgSts.BkTransparency==0) PSS.FillPattern|=0x80;
		                      else PSS.FillPattern&=~0x80;	      
              snprintf(cq->ObjType, sizeof(cq->ObjType),"DP Brush Back Color (%2X %2X %2X; %2X)", R,G,B, A);
              break;
              }
    case 0x35:{uint16_t Pattern;
	      Rd_word(cq->wpd,&Pattern);
	      snprintf(cq->ObjType, sizeof(cq->ObjType),"Brush Pattern %u", Pattern);
	      switch(Pattern)
	      {
	        case 0: PSS.FillPattern=FILL_SOLID; break;	// Solid pattern.
		case 1: PSS.FillPattern=FILL_GRAY_50; break;	// 50% Gray
		case 2: PSS.FillPattern=FILL_GRAY_25; break;	// 25% Gray
		case 3: PSS.FillPattern=FILL_GRAY_12; break;	// 12% Gray
		case 4: PSS.FillPattern=FILL_CROSS_HATCH; break;// CrossHatch
		case 5: PSS.FillPattern=FILL_CROSS_HATCH2;break;// CrossHatch2
		case 6: PSS.FillPattern=FILL_DIAG_CROSSHATCH; break;// Diagonal CrossHatch
		case 7: PSS.FillPattern=FILL_DIAG_CROSSHATCH2; break;// Diagonal CrossHatch2
		case 8: PSS.FillPattern=FILL_HORIZONTAL; break;	// Horizontal
		case 9: PSS.FillPattern=FILL_HORIZONTAL2; break;// Horizontal2
		case 10: PSS.FillPattern=FILL_VERTICAL; break;	// Vertical
		case 11: PSS.FillPattern=FILL_VERTICAL2; break;	// Vertical2
		case 12: PSS.FillPattern=FILL_DIAG_UP; break;	// Diagonal up
		case 13: PSS.FillPattern=FILL_DIAG_UP2; break;	// Diagonal2 up
		case 14: PSS.FillPattern=FILL_DIAG_DOWN; break;	// Diagonal down
		case 15: PSS.FillPattern=FILL_DIAG_DOWN2; break;// Diagonal2 down
		case 16: PSS.FillPattern=FILL_SQUARES; break;	// Small squares
		case 17: PSS.FillPattern=FILL_SQUARES2; break;	// Bigger squares
		case 18: PSS.FillPattern=FILL_BRICKS; break;	// Bricks
		case 19: PSS.FillPattern=FILL_DIAG_BRICKS; break;// Diagonal Bricks
		case 20: PSS.FillPattern=FILL_FISHSCALE; break;
		case 21: PSS.FillPattern=FILL_HONEYCOMB; break;	// Honeycomb
		case 22: PSS.FillPattern=FILL_PATIO; break;	// Patio
		case 23: PSS.FillPattern=FILL_CHAINLINK; break;
		case 24: PSS.FillPattern=FILL_WEAVE; break;	// Weave
		case 25: PSS.FillPattern=FILL_TARTAN; break;	// Plaid (Tartan)
		case 26: PSS.FillPattern=FILL_BALLS; break;	// Balls
		case 27: PSS.FillPattern=FILL_SM_SQUARES; break;// Hollow small squares
		case 28: PSS.FillPattern=FILL_PLUS; break;	// Plus
		case 29: PSS.FillPattern=FILL_TRIANGLES; break;	// Triangles
		case 30: PSS.FillPattern=FILL_WAVES; break;	// Waves
		case 31: PSS.FillPattern=FILL_ARCH; break;	// Arch
		default:if(cq->err && Pattern>=32)
			  {		/// @TODO There could be custom pattern defined in #9.
			  cq->perc.Hide();
			  fprintf(cq->err,_("\nWarning: Unexpected hatch pattern %d."),Pattern); 
			  }
			PSS.FillPattern = FILL_SOLID;
			break;
	      }
	      if(ImgSts.BkTransparency==0 && PSS.FillPattern!=FILL_NONE) PSS.FillPattern|=0x80;
	      break;}
    case 0x36:{
              strcpy(cq->ObjType,"Horizontal Line");
              VectorLine *pVecl = new VectorLine(2);
	      int16_t val;
              Rd_word(cq->wpd, (uint16_t*)&val);
              pVecl->Points[1] = pVecl->Points[3] = val;
              Rd_word(cq->wpd, (uint16_t*)&val);
              pVecl->Points[0] = val;
              Rd_word(cq->wpd, (uint16_t*)&val);
              pVecl->Points[2] = val;
              VectList.AddObject(pVecl); 
              break;
              }
    case 0x37:{
              strcpy(cq->ObjType,"Vertical Line");
              VectorLine *pVecl = new VectorLine(2);
	      int16_t val;
              Rd_word(cq->wpd, (uint16_t*)&val);
              pVecl->Points[0] = pVecl->Points[2] = val;
              Rd_word(cq->wpd, (uint16_t*)&val);
              pVecl->Points[1] = val;
              Rd_word(cq->wpd, (uint16_t*)&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:{uint16_t TextureId;
	      Rd_word(cq->wpd,&TextureId);
	      snprintf(cq->ObjType, sizeof(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: snprintf(cq->ObjType, sizeof(cq->ObjType),"?%d?",(int)Rec2.Type); break;
    }


	//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=%lXh", (long)cq->ActualPos);
    }
  if(*cq->ObjType=='!' || *cq->ObjType=='?')
	{UnknownObjects++;*cq->ObjType=0;}
}


/** 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
char NumFormat;
WPGHeader Hdr;
WPG_records WPG;
FILE *SrcImage;		///< Used for extracting image.
string NewFilename;
Image Img, *CurrImg;
uint32_t ResourceEnd;
APalette *Palette = NULL;
PS_State PSS;
FloatBBox bbx;
VectorList VectList;

  InitBBox(bbx);

  CurrImg = &Img;

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

    if(Filename==NULL || *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 = 1;
      Hdr.FileType = 0x16;
      Hdr.MajorVersion = 0x1;
      Hdr.MinorVersion = 0;
      Hdr.EncryptKey = Hdr.Reserved = 0;

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

    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);
    FillGray(Palette);

    char WpgFixTextL1 = 0;	///< There is sometimes duplicated a same WPG text L1 after Level2 text.
    WPGFigure Figure = {0,0,0,0,0,0};

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

	NumFormat = 0;
	if(WpgFixTextL1)
	  {
	  if(Rec.RecType!=2 && Rec.RecType!=0xD && Rec.RecType!=0xC) WpgFixTextL1=0;
	  }

	switch(Rec.RecType)
	  {
	  case 0x1: {PSS.FillPattern = fgetc(cq->wpd);
		     uint8_t 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);
			   }
		         }
		     snprintf(cq->ObjType, sizeof(cq->ObjType),"Fill Attributes(%u)",PSS.FillPattern);
		     break;
		     }
	  case  0x2:{strcpy(cq->ObjType,"Line Attributes");
		   // CrackObject(cq,NewObject);
		      uint8_t LineColor,LineStyle;
		      uint16_t LineWidth;

		      LineStyle = fgetc(cq->wpd);
		      if(PSS.LineStyle!=LineStyle) {PSS.LineStyle=LineStyle;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)LineStyle, (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");
		      float *Points = LoadPoints(cq, 2);
                      if(Points==NULL) break;
                      FixBoundingBox(Points, 2, bbx);
                      VectorLine *pVecLine = new VectorLine(Points, 2);
                      pVecLine->AttribFromPSS(PSS);
                      VectList.AddObject(pVecLine);
		      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);
		      if(Points==NULL) break;
		      FixBoundingBox(Points, WPG.Curve.Count,bbx);

                      VectorLine *pVecLine = new VectorLine(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecLine->AttribFromPSS(PSS);
                      pVecLine->Close = false;
                      VectList.AddObject(pVecLine); pVecLine=NULL;
		      break;
		      }
	  case  0x7:  {
		      strcpy(cq->ObjType,"Rectangle");
		      Rd_word(cq->wpd,&WPG.Rectangle.X);
		      Rd_word(cq->wpd,&WPG.Rectangle.Y);
		      Rd_word(cq->wpd,(uint16_t*)&WPG.Rectangle.Width);
		      Rd_word(cq->wpd,(uint16_t*)&WPG.Rectangle.Height);
		      UpdateBBox(bbx, 0, WPG.Rectangle.X, WPG.Rectangle.Y, WPG.Rectangle.Width, WPG.Rectangle.Height);
		      VectorRectangle *pRect = new VectorRectangle(
                          WPG.Rectangle.Y, WPG.Rectangle.Y+WPG.Rectangle.Height,
			  WPG.Rectangle.X, WPG.Rectangle.X+WPG.Rectangle.Width);
		      pRect->AttribFromPSS(PSS);
                      VectList.AddObject(pRect);
		      }
		      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);
		      if(Points==NULL) break;
                      FixBoundingBox(Points, WPG.Curve.Count, bbx);

                      VectorPolygon *pVecPoly = new VectorPolygon(Points, WPG.Curve.Count);
                      Points = NULL;
                      pVecPoly->AttribFromPSS(PSS);  
                      pVecPoly->Close = true;
                      VectList.AddObject(pVecPoly); pVecPoly=NULL;
		      free(Points);
		      break;
		      }
	  case  0x9:{strcpy(cq->ObjType,"Elipsis");
//		      CrackObject(cq,NewObject);
		      Rd_word(cq->wpd,(uint16_t*)&WPG.Ellipse.Sx);
		      Rd_word(cq->wpd,(uint16_t*)&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);

		      const float LineWidthD2 = PSu2WPGu(PSS.LineWidth) / 2.0f;
		      UpdateBBox(bbx, WPG.Ellipse.RotAngle,
				   WPG.Ellipse.Sx-WPG.Ellipse.rx-LineWidthD2, WPG.Ellipse.Sy-WPG.Ellipse.ry-LineWidthD2,
			           2*(WPG.Ellipse.rx+LineWidthD2), 2*(WPG.Ellipse.ry+LineWidthD2));

                      VectorEllipse *pVecEllipse = new VectorEllipse(WPG.Ellipse.Sy-WPG.Ellipse.ry, WPG.Ellipse.Sy+WPG.Ellipse.ry,
		      					       WPG.Ellipse.Sx-WPG.Ellipse.rx, 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,"!Reserved 0xA"); break;
	  case  0xB:  {
		      strcpy(cq->ObjType,"Bitmap l1");
		      LoadWPGBitmapType1(cq->wpd,WPG.BitmapType1);
		      Raster2DAbstract *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;}
		      if(CurrImg->Raster!=NULL)
			{
			CurrImg->Next = new Image;
			CurrImg = CurrImg->Next;
			}
                      CurrImg->AttachRaster(Raster);
		      if(Raster->GetPlanes()>1) CurrImg->AttachPalette(Palette);
		      break;
		      }

	  case  0xC:  {
		      //CrackObject(cq,NewObject);
		      uint16_t TextLength;	//  in bytes
		      uint16_t X;		// WORD X value of text position
		      uint16_t Y;		// WORD Y value of text position
		      Rd_word(cq->wpd,(uint16_t*)&TextLength);
		      Rd_word(cq->wpd,(uint16_t*)&X);
		      Rd_word(cq->wpd,(uint16_t*)&Y);
		      if(TextLength>0 && WpgFixTextL1==0)
		        {
		        TextContainer *pTxC = new TextContainer();
			pTxC->PosX = X;		//WPGu2PSu(X);
			pTxC->PosY = Y;		//WPGu2PSu(Y);			

			while(TextLength-->0)
			{
			  unsigned char ch = fgetc(cq->wpd);
			  if(ch>=' ' && ch<=127)
			    AddCharacterToContainer(pTxC, ch, NULL, NULL, cq, PSS);			      
			}

		        if(pTxC->isEmpty())
			  delete pTxC;
			else
		          {
			  UpdateBBox(bbx, 0, X, Y-0.1f*PSu2WPGu(PSS.FontSize),			// Calculate bbx in WPU.
				     0.5f*PSu2WPGu(PSS.FontSize), 1.2*PSu2WPGu(PSS.FontSize));
		          VectList.AddObject(pTxC);
			  }
		        }
		      
		      strcpy(cq->ObjType,"Graphics Text l1");
		      NumFormat=1; break; //This is a bug fix for Draw Perfect WPG
		      WpgFixTextL1 = 0;
		      }
	  case  0xD:{strcpy(cq->ObjType,"Text Attributes");
		      uint16_t TextWidth;		//Font character width (arbitrary units)
		      uint8_t TextColor;
		      Rd_word(cq->wpd,(uint16_t*)&TextWidth);
		      PSS.FontSize = WPGu2PSu(TextWidth);
		      fseek(cq->wpd, 17, 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));
			  if(Ei2)
			    {
			    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;
                            }
                          else
                            NoMemory(cq,sizeof(EmbeddedImg));
			  }
			}
		      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);
                      if(Points==NULL) break;
                      FixBoundingBox(Points, WPG.Curve.Count, bbx);

                      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);
                      VectList.AddObject(pVecLine);
		      break;
		      }
	  case 0x14:{
		      strcpy(cq->ObjType,"Bitmap l2");
		      LoadWPGBitmapType2(cq->wpd,WPG.BitmapType2);

		      Raster2DAbstract *Raster = CreateRaster2D(WPG.BitmapType2.Width,WPG.BitmapType2.Height,WPG.BitmapType2.Depth);
		      if(Raster==NULL) {NoImgMemory(cq,WPG.BitmapType2.Width,WPG.BitmapType2.Height);break;}

		      if(UnpackWPGRaster(Raster,cq->wpd)<0)
				{delete Raster;Raster=NULL;break;}

		      VectorRaster *vr;
		      int RotAngle = WPG.BitmapType2.RotAngle & 0x0FFF;

		      if(Figure.LowLeftX==Figure.UpRightX || Figure.LowLeftY==Figure.UpRightY)
		        {	// limit scope for variables.
			const float CosAlpha = cos(DEG2RAD(RotAngle));
			const float SinAlpha = sin(DEG2RAD(RotAngle));
			const int dxRot = WPG.BitmapType2.UpRightX - WPG.BitmapType2.LowLeftX;
			const int dyRot = WPG.BitmapType2.UpRightY - WPG.BitmapType2.LowLeftY;

			float dx =  dxRot*CosAlpha + dyRot*SinAlpha;	// Rotate backward RotAngle.
			float dy = -dxRot*SinAlpha + dyRot*CosAlpha;

		        if(WPG.BitmapType2.RotAngle & 0x8000)
			   {Flip1D(Raster);RotAngle=360-RotAngle;}
			if(WPG.BitmapType2.RotAngle & 0x2000)
			   {
			   Flip2D(Raster);
			   if((WPG.BitmapType2.RotAngle & 0x8000)==0)
				RotAngle = 360-RotAngle;
			   }
			if(dy<0)
		            Flip2D(Raster);

		        vr = new VectorRaster((WPG.BitmapType2.UpRightX+WPG.BitmapType2.LowLeftX)/2.0f, (WPG.BitmapType2.UpRightY+WPG.BitmapType2.LowLeftY)/2.0f,
					dx, dy, DEG2RAD(RotAngle));
			vr->AttachRaster(Raster);		// Ussage count increased to 1.
			
			UpdateBBox(bbx, RotAngle, vr->CenterX-dx/2, vr->CenterY-dy/2, dx, dy);  // Calculate bbx in [WPU].
			}
		      else		// I have absolutelly no clue how to place raster in this case.
			{		// The best guess is to use outer bounding box and try to calculate raster inner position.
			  if(WPG.BitmapType2.RotAngle & 0x8000)
			    {Flip1D(Raster);RotAngle=360-RotAngle;}
			  if(WPG.BitmapType2.RotAngle & 0x2000)
			    {
			    Flip2D(Raster);
			    if((WPG.BitmapType2.RotAngle & 0x8000)==0)
				RotAngle = 360-RotAngle;
			    }
			  //if(Figure.UpRightY < Figure.LowLeftY)
			  //    Flip2D(Raster);
			  vr = new VectorRaster(Raster, (Figure.LowLeftX+Figure.UpRightX)/2, (Figure.LowLeftY+Figure.UpRightY)/2,
							 Figure.UpRightX-Figure.LowLeftX, Figure.UpRightY-Figure.LowLeftY, DEG2RAD(RotAngle));
			  UpdateBBox(bbx, 0, Figure.LowLeftX, Figure.LowLeftY, Figure.UpRightX-Figure.LowLeftX, Figure.UpRightY-Figure.LowLeftY); 
			  memset(&Figure, 0, sizeof(Figure));
			}		      
		      
	              if(Raster->GetPlanes()>1) vr->AttachPalette(Palette);
                      VectList.AddObject(vr);

		      /* VectorRectangle *vrr;
		      vrr = new VectorRectangle(WPG.BitmapType2.UpRightY, WPG.BitmapType2.LowLeftY, WPG.BitmapType2.UpRightX, WPG.BitmapType2.LowLeftX);
		      vrr->BrushStyle = 0;
		      vrr->LineColor.Red = 0;
		      VectList.AddObject(vrr); */
		      }
		      break;
	  case 0x15:  LoadWPGFigure(cq->wpd,Figure);
		      /*VectorRectangle *vrr;
		      vrr = new VectorRectangle(Figure.UpRightY, Figure.LowLeftY, Figure.UpRightX, Figure.LowLeftX);
		      vrr->BrushStyle = 0;
		      vrr->LineColor.Green = 0;
		      VectList.AddObject(vrr);*/
		      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);
		      long 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,(uint16_t*)&WPG.TextL2.LowLeftX);
			Rd_word(cq->wpd,(uint16_t*)&WPG.TextL2.LowLeftY);
			Rd_word(cq->wpd,(uint16_t*)&WPG.TextL2.UpRightX);
			Rd_word(cq->wpd,(uint16_t*)&WPG.TextL2.UpRightY);
			// [XScale] [YScale] (Type)

#ifdef _DEBUG			// Debug purpose rectangle
/*			VectorRectangle *Vr = new VectorRectangle(WPG.TextL2.LowLeftY, WPG.TextL2.UpRightY, WPG.TextL2.LowLeftX, WPG.TextL2.UpRightX);
			Vr->AttribFromPSS(PSS);
			VectList.AddObject(Vr); */
#endif			
			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.TextL2);
			//{FILE *F = fopen("C:\\temp\\29\\DumpXX.txt","wb");VectList.Dump2File(F);fclose(F);}
			WpgFixTextL1 = 1;
			}
		      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));
                        if(Ei2!=NULL)
                          {
			  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;
                          }
			else NoMemory(cq,sizeof(EmbeddedImg));
			}
		     break;
	  //case 0x24: CrackObject(cq,NewObject);
	  default: snprintf(cq->ObjType, sizeof(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(unsigned i=0; i<Rec.RecordLength; i++)
		      {
		      const char 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;				// Prevent double execution.
#ifdef _DEBUG
    //{FILE *F = fopen("O:\\temp\\22\\DumpWPG1.txt","wb");VectList.Dump2File(F);fclose(F);}
#endif
    }

  if(Box.Image_type==2 && Box.Image_size>0) //WPG level 2 (might contain l.1 or different content)
    {
    WPG2Start StartWPG2;
    WPG2Record Rec2;
    //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,"ddbbbbww",
	      &Hdr.FileId, &Hdr.DataOffset, &Hdr.ProductType, &Hdr.FileType,
	      &Hdr.MajorVersion, &Hdr.MinorVersion, &Hdr.EncryptKey, &Hdr.Reserved);
    if(Hdr.FileId!=0x435057FF || Hdr.EncryptKey!=0) goto NoCopyImage;

    if(Hdr.FileType==0x30)
      {
      cq->perc.Hide();
      if(SaveWPG>=1)
        {
	printf("\n!!!! MAC WPG1 detected!!!! %X",(unsigned)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(unsigned i=0; i<Box.Image_size; i++)
	   {
	   const char 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 + Hdr.DataOffset;
      fseek(cq->wpd, cq->ActualPos, SEEK_SET);
      while(cq->ActualPos<ResourceEnd)
	{	
	RdDWORD_HiEnd(&Rec2.RecordLength, cq->wpd);
	uint32_t 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=%lXh", (long)cq->ActualPos);
	  }
	cq->ActualPos = (NewObject+1) & ~1;
        fseek(cq->wpd,cq->ActualPos,SEEK_SET);
	}
      goto NoCopyImage;
      }

    if(Hdr.FileType!=0x16)  goto NoCopyImage;
    if(Hdr.MajorVersion==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.MajorVersion!=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 = 1;			// Solid contour line as default.
    PSS.FillPattern = FILL_SOLID;	// Solid fill
    PSS.FirstTimeFix = true;
    memset(&PSS.FillColor, 0xFF, sizeof(PSS.FillColor));
    memset(&PSS.FillBackground, 0xFF, sizeof(PSS.FillBackground));
    TWPG2ConvStatus ImgSts;

    //VectorList VectList;
    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);
	    uint32_t NewObject = cq->ActualPos+Rec2.RecordLength;

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

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

	    if(NewObject!=ftell(cq->wpd))
		    fseek(cq->wpd,NewObject,SEEK_SET);
	    cq->ActualPos = NewObject;
	    }
#ifdef _DEBUG
    //{FILE *F = fopen("O:\\temp\\22\\DumpInWPUs.txt","wb");VectList.Dump2File(F);fclose(F);}
#endif
    Box.Image_size=0;
    }

 if(VectList.VectorObjects > 0)		//Transfer PostScript part into Image
   {
   //{FILE *F = fopen("C:\\temp\\34\\DumpInWPUs.txt","wb");VectList.Dump2File(F);fclose(F);}
   VectList.Transform(vecResizeXY(WPGu2PSu(1)));	// Scale contents from WPGu to PSu.
   //{FILE *F = fopen("C:\\temp\\29\\DumpInPSUs.txt","wb");VectList.Dump2File(F);fclose(F);}

   if(!Img.isEmpty())
	{
	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;}

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;
uint32_t ActualPos_Backup;
EmbeddedImg *EmImTmp, *EmImg=NULL;
Image Img;
int8_t 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.isEmpty())   // last chance - try to load any file format
  {
    if(strcmp(cq->ObjType, "End WPG") != 0) // "End WPG" means that a file is a valid WPG, further search is futile.
        Img = LoadPicture(FileName);
    else
        fprintf(cq->err, _("\nWarning: Valid WPG file seems to be empty!"));
  }

  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.isEmpty()) return(Img);
   if(strcmp(cq->ObjType, "End WPG") == 0)
       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) ",chk(Filename));fflush(cq->log);
#endif
unsigned i;
int lines=0;
const char *ss;
string NewFilename;
string FileExtension;
Image Img;
EmbeddedImg *EmImTmp,*EmImg=NULL;
FILE *SrcImage;
bool bFileIsOK = false;

  if(InputPS<STYLE_NOTUSED) 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.isEmpty())
     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++)
	  {
	    string tmp = string(ImgInputDirs[i]) + Filename;
	    if(!CopyFile(NewFilename,tmp))
              {
              bFileIsOK = true;
              goto FileIsOK;
              }
	  }
	}
     }

  if(!Img.isEmpty())	// Convert raster image into the PostScript
	{
SavePS: 
#ifndef NO_IMG
	AutoOrient(&Img);	// Ensure to apply orientation attribute from EXIF.
	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);
	      cq1_new->Convert_first_pass();	// failure check to add >=1 
	      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.isEmpty()) 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.isEmpty()) 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 == LEAVE_ONE_EMPTY_LINE)    /* Left one empty line for new enviroment */
	{
	fputc('%', cq->strip);
	NewLines(cq,2);
	}
  if(cq->char_on_line==CHAR_PRESENT)    /* 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>=STYLE_NOTUSED) Rotate=STYLE_USED;
                             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
uint32_t fsize;
TBox Box;
int8_t 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--------*/
