/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    module for conversion SVG files into LaTeX		      *
 * modul:       pass1svg.cc                                                   *
 * description: This module contains parser for SVG documents. It could   *
 *		be optionally compiled with WP2LaTeX package.		      *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "stringa.h"
#include "lists.h"
#include "dbllist.h"
#include "matrix.h"

#include "images/raster.h"
#include "images/vecimage.h"
#include "images.h"

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

#include "wmfsupp.h"


#ifndef M_PI
 #define M_PI        3.14159265358979323846
#endif


extern list HTMLChars;
int SavePictureEPS(const char *Name, const Image &Img);


/// Read float value from in memory string.
/// @param[in,out] pstr	Pointerto a stringto be read.
/// @param[out] f	Value read.
/// @return	true on success, false on failure.
bool ReadFloat(const char **pstr, float &f)
{
  if(pstr==NULL) {f=0;return false;}
  const char *str = *pstr;
  if(str==NULL) {f=0;return false;}

  while(isspace(*str)) str++;

  if(!isdigit(*str) && *str!='.' && *str!='-')
  {
    *pstr = str;
    f = 0;
    return false;
  }
  f = atof(str);

  if(*str=='-') str++;
  while(isdigit(*str) || *str=='.') str++;

  while(isspace(*str)) str++;

  *pstr = str;
return true;
}


inline float DEG2RAD(const float x) {return((x)*(float)M_PI/180.0f);}


/** Functor class for SVG transform. */
class TransformSVG: public AbstractTransformXY
{
public:
  float_matrix CTM;

  TransformSVG(void): CTM(4,4,(float)0) {CTM(0,0)=CTM(1,1)=CTM(2,2)=CTM(3,3)=1;}

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

  float TopPBottom;
};


void TransformSVG::ApplyTransform(float &x, float &y) const
{
  const float xNew = CTM(0,0)*x + CTM(1,0)*y + CTM(3,0);
  y = CTM(0,1)*x + CTM(1,1)*y + CTM(3,1);
  x = xNew;
}

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

/*Register translators here*/
class TconvertedPass1_SVG: virtual public TconvertedPass1_XML, virtual public TconvertedPass1_Image
     {
protected:
     VectorList VectList;
     PS_State PSS;
     float PositionX, PositionY;

     bool GetProp(const char *PropName, float &f);
     void GetColor(RGB_Record &LineColor);
     void GetLineCap(unsigned char &LineCap);
     void GetLineJoin(unsigned char &LineJoin);
     float *LoadPoints(int &n);
     AbstractTransformXY *getTransform(void);
     void ClosePathLine(float **pPoints, int & n);
     void ClosePathCurve(float **pPoints, int & n);

public:
     virtual int Convert_first_pass(void);
     void ProcessKeySVG(void);

     void Circle(void);
     void Ellipse(void);
     void Group(void);
     void Line(void);
     void Path(void);
     void Polygon(void);
     void PolyLine(void);
     void Rectangle(void);
     void Text(void);
     };
TconvertedPass1 *Factory_SVG(void) {return new TconvertedPass1_SVG;}
FFormatTranslator FormatSVG("SVG",Factory_SVG);

#define SVGVersion "0.1"


bool TconvertedPass1_SVG::GetProp(const char *PropName, float &f)
{
int i;

  if((i=TAG_Args IN PropName)<0) return false;
  const char * const Payload = TAG_Args.Member(i,1);
  if(Payload==NULL) return false;
  f = atof(Payload);
return true;
}

typedef struct
{
  const char *desc;
  RGB_Record color;
} SVG_color;

// https://www.w3.org/TR/css-color-3/#html4
static SVG_color SVG_colors[] =
{
 "black", {0,0,0},
 "silver", {192,192,192},
 "gray", {128,128,128},
 "white", {255,255,255},
 "maroon", {128,0,0},
 "red", {255,0,0},
 "purple", {128,0,128},
 "fuchsia", {255,0,255},
 "green", {0,128,0},
 "lime", {0,255,0},
 "olive" , {128,128,0},
 "yellow", {255,255,0},
 "navy", {0,0,128},
 "blue", {0,0,255},
 "teal", {0,128,128},
 "aqua", {0,255,255}
};


void TconvertedPass1_SVG::GetColor(RGB_Record &LineColor)
{
int i;

  if((i=TAG_Args IN "stroke")<0) return;
  const char * const Payload = TAG_Args.Member(i,1);
  if(Payload==NULL) return;

  for(i=0; i<sizeof(SVG_colors)/sizeof(SVG_color);i++)
    if(!strcmp(Payload, SVG_colors[i].desc))
    {
      LineColor = SVG_colors[i].color;
      return;
    }
}


void TconvertedPass1_SVG::GetLineCap(unsigned char &LineCap)
{
int i;

  if((i=TAG_Args IN "stroke-linecap")<0) return;
  const char * const Payload = TAG_Args.Member(i,1);
  if(Payload==NULL) return;

  if(!strcmp(Payload, "butt")) {LineCap=0; return;}
  if(!strcmp(Payload, "round")) {LineCap=1; return;}
  if(!strcmp(Payload, "square")) {LineCap=2;}
}


void TconvertedPass1_SVG::GetLineJoin(unsigned char &LineJoin)
{
int i;

  if((i=TAG_Args IN "stroke-linejoin")<0) return;
  const char * const Payload = TAG_Args.Member(i,1);
  if(Payload==NULL) return;

  if(!strcmp(Payload, "miter")) {LineJoin=0; return;}
  if(!strcmp(Payload, "miter-clip")) {LineJoin=0; return;} //!!!!
  if(!strcmp(Payload, "round")) {LineJoin=1; return;}
  if(!strcmp(Payload, "bevel")) {LineJoin=2;}
  //if(!strcmp(Payload, "arcs")) {LineJoin=;}
}


float *TconvertedPass1_SVG::LoadPoints(int &n)
{
int i;

  n = 0;
  if((i=TAG_Args IN "points")<0) return NULL;
  const char * const StrPoints = TAG_Args.Member(i,1);
  if(StrPoints==NULL) return NULL;

  i = 0;
  while(StrPoints[i] != 0)
  {
    if(isdigit(StrPoints[i]))
    {
      n++;
      do
        { i++;}
      while(isdigit(StrPoints[i]));
      continue;
    }
    i++;
  }
  if(n<=1) return NULL;

  float *Points = (float*)malloc(sizeof(float)*n);
  if(Points == NULL) return NULL;

  n = i = 0;
  while(StrPoints[i] != 0)
  {
    if(isdigit(StrPoints[i]))
    {
      Points[n++] = atof(StrPoints+i);
      do
        { i++;}
      while(isdigit(StrPoints[i]));
      continue;
    }
    i++;
  }

  n/=2;
  for(i=1; i<n; i++)
    UpdateBBox(bbx,0, Points[2*i], Points[2*i+1], 0, 0);
return Points;
}


AbstractTransformXY *TconvertedPass1_SVG::getTransform(void)
{
int i;

  if((i=TAG_Args IN "transform")<0) return NULL;
  const char * StrTransform = TAG_Args.Member(i,1);
  if(StrTransform==NULL) return NULL;
  while(isspace(*StrTransform)) StrTransform++;
  if(*StrTransform==0) return NULL;

  TransformSVG *pTransformSVG = new TransformSVG;
  while(*StrTransform!=0)
  {
    if(isspace(*StrTransform)) {StrTransform++; continue;}

    if(!strncmp("translateX",StrTransform,10))
    {
      StrTransform += 10;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      if(ReadFloat(&StrTransform, Tx(3,0)))
      {
        Tx(0,0) = Tx(1,1) = Tx(2,2) = Tx(3,3) = 1;
        pTransformSVG->CTM *= Tx;
      }
    } else if(!strncmp("translateY",StrTransform,10))
    {
      StrTransform += 10;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      if(ReadFloat(&StrTransform, Tx(3,1)))
      {
        Tx(0,0) = Tx(1,1) = Tx(2,2) = Tx(3,3) = 1;
        pTransformSVG->CTM *= Tx;
      }
    } else if(!strncmp("translate",StrTransform,9))
    {
      StrTransform += 9;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      if(ReadFloat(&StrTransform, Tx(3,0)))
      {
        Tx(0,0) = Tx(1,1) = Tx(2,2) = Tx(3,3) = 1;
        if(*StrTransform==',')
        {
          StrTransform++;
          if(ReadFloat(&StrTransform, Tx(3,1)))
          {
            pTransformSVG->CTM *= Tx;
          }
        }
      }
    } else if(!strncmp("scaleX",StrTransform,6))
    {
      StrTransform += 6;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      if(ReadFloat(&StrTransform, Tx(0,0)))
      {
        Tx(1,1) = Tx(2,2) = Tx(3,3) = 1;
        pTransformSVG->CTM *= Tx;
      }
    } else if(!strncmp("scaleY",StrTransform,6))
    {
      StrTransform += 6;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      if(ReadFloat(&StrTransform, Tx(1,1)))
      {
         Tx(0,0) = Tx(2,2) = Tx(3,3) = 1;
         pTransformSVG->CTM *= Tx;
      }
    } else if(!strncmp("scale",StrTransform,5))
    {
      StrTransform += 5;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform=='(')
      {
        StrTransform++;
        float_matrix Tx(4,4,(float)0);
        if(ReadFloat(&StrTransform, Tx(0,0)))
        {
           Tx(2,2) = Tx(3,3) = 1;
           if(*StrTransform==',')
           {
             StrTransform++;
             if(ReadFloat(&StrTransform, Tx(1,1)))
             {
               pTransformSVG->CTM *= Tx;
             }
           }
        }
      }
    } else if(!strncmp("rotate",StrTransform,6))
    {
      StrTransform += 6;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float angle;
      if(ReadFloat(&StrTransform,angle))
      {
        if(fabs(angle) > 1e-4)
        {
          angle = DEG2RAD(angle);
          float_matrix Tx(4,4,(float)0);
          Tx(2,2) = Tx(3,3) = 1;
          Tx(1,1) = Tx(0,0) = cos(angle);
          Tx(0,1) = sin(angle);
          Tx(1,0) =-Tx(0,1);
          pTransformSVG->CTM *= Tx;
        }
      }
    } else if(!strncmp("matrix",StrTransform,6))	// Only 3x2 matrix is supported, 4x4 not.
    {
      StrTransform += 6;
      while(isspace(*StrTransform)) {StrTransform++;}
      if(*StrTransform!='(') continue;
      StrTransform++;
      float_matrix Tx(4,4,(float)0);
      Tx(2,2) = Tx(3,3) = 1;

      bool Result = ReadFloat(&StrTransform, Tx(0,0));
      Result &= ReadFloat(&StrTransform, Tx(0,1));

      Result &= ReadFloat(&StrTransform, Tx(1,0));
      Result &= ReadFloat(&StrTransform, Tx(1,1));

      Result &= ReadFloat(&StrTransform, Tx(3,0));
      Result &= ReadFloat(&StrTransform, Tx(3,1));

      if(Result)
      {
        pTransformSVG->CTM *= Tx;
      }
    } else
    {			// No keyword recognised; skip one character.
      StrTransform++;
      continue;
    }

    while(*StrTransform!=')')
    {
      if(*StrTransform==0) break;
          StrTransform++;
    }
    if(*StrTransform==')') StrTransform++;

//skew(0, 0), skewX(0), skewY(0)
//   1 tg(a) 0. 0      1     0  0. 0
//   0   1   0. 0      tg(b) 1  0  0
//X  0   0   1  0   Y  0   0   1  0
//   0...0   0  1      0...0   0  1
  }

return pTransformSVG;
}


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


//static void ProcessKeySVG(TconvertedPass1_XML *cq);


/*This function extracts some information from meta ?xml tag*/
/*
static void MetaXML(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#MetaXML() ");fflush(cq->log);
#endif
int i;
char *charset;
  //strcpy(cq->ObjType, "?xml");

  if((i=cq->TAG_Args IN "encoding")>=0)
	{
	charset = cq->TAG_Args.Member(i,1);
	if(charset==NULL) return;

	cq->SelectTranslator(charset);
	}
}
*/


void TconvertedPass1_SVG::Circle(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Rectangle ");fflush(log);
#endif
static const char ObjName[] = "!Circle";
float	cx, cy, r;
float stroke_width;

  if(!GetProp("cx",cx)) cx=PositionX;
  if(!GetProp("cy",cy)) cy=PositionY;
  if(GetProp("r",r))
  {
    UpdateBBox(bbx, 0, cx-r, cy-r, 2*r, 2*r);

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

    VectorEllipse *pVectCirc = new VectorEllipse(cy-r, cy+r, cx+r, cx-r);
    pVectCirc->AttribFromPSS(PSS);
    if(GetProp("stroke-width",stroke_width))
    {
      pVectCirc->PenWidth = stroke_width;
    }
    GetColor(pVectCirc->LineColor);
    GetLineCap(pVectCirc->LineCap);
    GetLineJoin(pVectCirc->LineJoin);
    VectList.AddObject(pVectCirc);

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

  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_SVG::Ellipse(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Ellipse ");fflush(log);
#endif
static const char ObjName[] = "!Ellipse";
float	cx, cy, rx, ry;
float stroke_width;

  if(!GetProp("cx",cx)) cx=PositionX;
  if(!GetProp("cy",cy)) cy=PositionY;
  if(!GetProp("rx",rx)) goto ExitErr;
  if(!GetProp("ry",ry)) goto ExitErr;

  UpdateBBox(bbx, 0, cx-rx, cy-ry, 2*rx, 2*ry);

  {
    VectorEllipse *pVectEll = new VectorEllipse(cy-ry, cy+ry, cx+rx, cx-rx);
    pVectEll->AttribFromPSS(PSS);
    if(GetProp("stroke-width",stroke_width))
    {
      pVectEll->PenWidth = stroke_width;
    }
    GetColor(pVectEll->LineColor);
    GetLineCap(pVectEll->LineCap);
    GetLineJoin(pVectEll->LineJoin);
    VectList.AddObject(pVectEll);
  }
  strcpy(ObjType,ObjName+1);
  return;

ExitErr:
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_SVG::Group(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Group ");fflush(log);
#endif
VectorList VectListIn;
FloatBBox bbxBk = bbx;
RGB_Record LineColorBK = PSS.LineColor;
unsigned char LineCapBK = PSS.LineCap;
unsigned char LineJoinBK = PSS.LineJoin;

  GetLineCap(PSS.LineCap);
  GetLineJoin(PSS.LineJoin);
  GetColor(PSS.LineColor);
  AbstractTransformXY *pTRx = getTransform();

  bbx.MinX=65537; bbx.MaxX=-1; bbx.MinY=65537; bbx.MaxY=-1;
  VectList.Swap(VectListIn);
  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if(by==XML_closetag && TAG=="</g>")
        break;
    ProcessKeySVG();
  }
  recursion--;
  VectList.Swap(VectListIn);

  if(VectListIn.VectorObjects > 0)
  {		// Bounding box is in PS units.
    if(pTRx != NULL)
    {
      pTRx->ApplyTransform(bbx.MinX,bbx.MinY);
      pTRx->ApplyTransform(bbx.MaxX,bbx.MaxY);
      VectListIn.Transform(*pTRx);
    }
    if(VectList.VectorObjects > 0)
    {
      UpdateBBox(bbx,0, bbxBk.MinX,bbxBk.MinY, bbxBk.MaxX-bbxBk.MinX, bbxBk.MaxY-bbxBk.MinY);
    }
    VectList.Append(VectListIn);
  }
  else
    bbx = bbxBk;
  if(pTRx != NULL) {delete(pTRx); pTRx=NULL;}

  PSS.LineCap = LineCapBK;
  PSS.LineJoin = LineJoinBK;
  PSS.LineColor = LineColorBK;

strcpy(ObjType,"Group");
}


void TconvertedPass1_SVG::Text(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Text ");fflush(log);
#endif
string txt;
float x, y;

  if(!GetProp("x",x)) x=0;
  if(!GetProp("y",y)) y=0;

  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if(by==XML_closetag && TAG=="</text>")
        break;
    if(by==XML_char)
    {
      txt += subby;
      continue;
    }
    ProcessKeySVG();
  }
  recursion--;

  if(!txt.isEmpty())
  {
    TextContainer *pTextCont = new TextContainer;
    pTextCont->PosX = x;
    pTextCont->PosY = y;
    pTextCont->AddText(temp_string(txt),PSS);
    VectList.AddObject(pTextCont);
    UpdateBBox(bbx,0, x,y, 0,0);
  }

strcpy(ObjType,"Group");
}


void TconvertedPass1_SVG::Line(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Line ");fflush(log);
#endif
static const char ObjName[] = "!Line";
float *Points = (float*)malloc(4*sizeof(float));
float stroke_width;

  if(Points==NULL) goto ExitErr;
  if(!GetProp("x1",Points[0])) Points[0]=PositionX;
  if(!GetProp("y1",Points[1])) Points[1]=PositionY;
  if(!GetProp("x2",Points[2])) goto ExitErr;
  if(!GetProp("y2",Points[3])) goto ExitErr;
  
  {
    VectorLine *pVecLine = new VectorLine(Points, 2); Points=NULL;
    pVecLine->AttribFromPSS(PSS);
    if(GetProp("stroke-width",stroke_width))
    {
      pVecLine->PenWidth = stroke_width;
    }
    GetColor(pVecLine->LineColor);
    GetLineCap(pVecLine->LineCap);
    GetLineJoin(pVecLine->LineJoin);
    VectList.AddObject(pVecLine);
  }
  strcpy(ObjType,ObjName+1);
  return;

ExitErr:
  if(Points!=NULL)
    {free(Points); Points=NULL;}
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_SVG::ClosePathLine(float **pPoints, int & n)
{
int i;
float stroke_width;

  if(*pPoints==NULL) return;
  if(n<=0)
  {
    free(*pPoints); *pPoints=NULL;
    return;
  }

  (*pPoints)[0]=PositionX; (*pPoints)[1]=PositionY;
  for(i=0; i<n; i++)
      UpdateBBox(bbx,0, (*pPoints)[2*i], (*pPoints)[2*i+1], 0, 0);
  VectorLine *pVecLine = new VectorLine(*pPoints, n); n=0;
  *pPoints = NULL;
  pVecLine->AttribFromPSS(PSS);
  if(GetProp("stroke-width",stroke_width))
  {
    pVecLine->PenWidth = stroke_width;
  }
  GetColor(pVecLine->LineColor);
  GetLineCap(pVecLine->LineCap);
  GetLineJoin(pVecLine->LineJoin);
  VectList.AddObject(pVecLine);
}


void TconvertedPass1_SVG::ClosePathCurve(float **pPoints, int & n)
{
int i;
float stroke_width;

  if(*pPoints==NULL) return;
  if(n<=0)
  {
    free(*pPoints); *pPoints=NULL;
    return;
  }

  (*pPoints)[0]=PositionX; (*pPoints)[1]=PositionY;
  for(i=0; i<n; i++)
      UpdateBBox(bbx,0, (*pPoints)[6*i], (*pPoints)[6*i+1], 0, 0);
  VectorCurve *pVecCurve = new VectorCurve(*pPoints, 3*n+1); n=0;
  *pPoints = NULL;
  pVecCurve->AttribFromPSS(PSS);
  if(GetProp("stroke-width",stroke_width))
  {
    pVecCurve->PenWidth = stroke_width;
  }
  GetColor(pVecCurve->LineColor);
  GetLineCap(pVecCurve->LineCap);
  GetLineJoin(pVecCurve->LineJoin);
  VectList.AddObject(pVecCurve);
}


void TconvertedPass1_SVG::Path(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Path ");fflush(log);
#endif
static const char ObjName[] = "!Path";
float *Points = NULL;
int n, i;
const char *StrMovements;
bool isCurve = false;

  n = 0;
  if((i=TAG_Args IN "d")<0) goto ExitErr;
  StrMovements = TAG_Args.Member(i,1);
  if(StrMovements==NULL) goto ExitErr;

  while(*StrMovements != 0)
  {
    if(*StrMovements=='M')	//move to.
    {
      StrMovements++;

      if(isCurve)
        ClosePathCurve(&Points,n);
      else
        ClosePathLine(&Points,n);

      ReadFloat(&StrMovements, PositionX);
      if(*StrMovements==',') StrMovements++;
      while(isspace(*StrMovements)) StrMovements++;
      if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-')
      {
        ReadFloat(&StrMovements, PositionY);
      }
      continue;
    }

    if(*StrMovements=='L')	//line to.
    {
      StrMovements++;
      while(isspace(*StrMovements)) StrMovements++;
      if(isCurve)
      {
        ClosePathCurve(&Points,n);
        isCurve = false;
      }
      if(Points==NULL)
      {
        Points=(float*)malloc(4*sizeof(float));
        n = 2;
      }
      else
      {
        n++;
        Points=(float*)realloc(Points,2*n*sizeof(float));
      }
      if(Points==NULL) goto ExitErr;

      ReadFloat(&StrMovements, Points[2*n-2]);
      if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-')
      {
        ReadFloat(&StrMovements, Points[2*n-1]);
      }
      else
        Points[2*n-1] = PositionY;

      continue;
    }

    if(*StrMovements=='C')	//curve to.
    {
      StrMovements++;
      while(isspace(*StrMovements)) StrMovements++;
      if(!isCurve)
      {
        ClosePathLine(&Points,n);
        isCurve = true;
      }
      if(Points==NULL)
      {
        Points=(float*)malloc(8*sizeof(float));
        n = 1;
      }
      else
      {
        n++;
        Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
      }
      if(Points==NULL) goto ExitErr;

      ReadFloat(&StrMovements, Points[2+6*n-6]);
      if(*StrMovements==',') StrMovements++;
      ReadFloat(&StrMovements, Points[2+6*n-5]);
      ReadFloat(&StrMovements, Points[2+6*n-4]);
      if(*StrMovements==',') StrMovements++;
      ReadFloat(&StrMovements, Points[2+6*n-3]);
      ReadFloat(&StrMovements, Points[2+6*n-2]);
      if(*StrMovements==',') StrMovements++;
      ReadFloat(&StrMovements, Points[2+6*n-1]);

      continue;
    }

    if(*StrMovements=='S')	// shorter curve to.
    {
      StrMovements++;
      while(isspace(*StrMovements)) StrMovements++;
      if(!isCurve)
      {
        ClosePathLine(&Points,n);
        isCurve = true;
      }
      if(Points==NULL)
      {
        Points=(float*)malloc(8*sizeof(float));
        n = 1;
      }
      else
      {
        n++;
        Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
      }

      if(Points==NULL) goto ExitErr;

      ReadFloat(&StrMovements, Points[2+6*n-4]);
      if(*StrMovements==',') StrMovements++;
      ReadFloat(&StrMovements, Points[2+6*n-3]);
      ReadFloat(&StrMovements, Points[2+6*n-2]);
      if(*StrMovements==',') StrMovements++;
      ReadFloat(&StrMovements, Points[2+6*n-1]);
      if(n==1)
      {
        Points[2+6*n-6] = Points[2+6*n-4];
        Points[2+6*n-5] = Points[2+6*n-3];
      }
      else
      {
        Points[2+6*n-6] = 2*Points[2+6*n-8] - Points[2+6*n-10];
        Points[2+6*n-5] = 2*Points[2+6*n-7] - Points[2+6*n-9];
      }
      continue;
    }

    StrMovements++;
  }

  if(isCurve)
    ClosePathCurve(&Points,n);
  else
    ClosePathLine(&Points,n);

  strcpy(ObjType,ObjName+1);
  return;

ExitErr:
  if(Points) {free(Points);Points=NULL;}
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_SVG::PolyLine(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::PolyLine ");fflush(log);
#endif
static const char ObjName[] = "!PolyLine";
float *Points;
float stroke_width;
int n;

  Points = LoadPoints(n);
  if(Points!=NULL)
  {

    VectorLine *pVecLine = new VectorLine(Points, n); Points=NULL;
    pVecLine->AttribFromPSS(PSS);
    if(GetProp("stroke-width",stroke_width))
    {
      pVecLine->PenWidth = stroke_width;
    }
    GetColor(pVecLine->LineColor);
    GetLineCap(pVecLine->LineCap);
    VectList.AddObject(pVecLine);

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

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_SVG::Polygon(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Polygon ");fflush(log);
#endif
static const char ObjName[] = "!Polygon";
float *Points;
float stroke_width;
int n;

  Points = LoadPoints(n);
  if(Points!=NULL)
  {

    VectorLine *pVecLine = new VectorLine(Points, n); Points=NULL;
    pVecLine->AttribFromPSS(PSS);
    if(GetProp("stroke-width",stroke_width))
    {
      pVecLine->PenWidth = stroke_width;
    }
    GetColor(pVecLine->LineColor);
    GetLineCap(pVecLine->LineCap);
    pVecLine->Close = true;
    VectList.AddObject(pVecLine);

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

  strcpy(ObjType,ObjName);
}


void TconvertedPass1_SVG::Rectangle(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Rectangle ");fflush(log);
#endif
static const char ObjName[] = "!Rectangle";
float	x, y, Width, Height;
float stroke_width;
VectorRectangle *pVectRec;

  if(!GetProp("x",x)) goto ExitErr;
  if(!GetProp("y",y)) goto ExitErr;
  if(!GetProp("width",Width)) goto ExitErr;
  if(!GetProp("height",Height)) goto ExitErr;

  UpdateBBox(bbx, 0, x, y, Width, Height);

  pVectRec = new VectorRectangle(x, y, x+Width, y+Height);
  pVectRec->AttribFromPSS(PSS);
  if(GetProp("stroke-width",stroke_width))
  {
    pVectRec->PenWidth = stroke_width;
  }
  GetColor(pVectRec->LineColor);
  VectList.AddObject(pVectRec);

  strcpy(ObjType,ObjName+1);
  return;

ExitErr:
  strcpy(ObjType,ObjName+1);
}


void TconvertedPass1_SVG::ProcessKeySVG(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG:::ProcessKeySVG() ");fflush(log);
#endif
string loc_TAG;
const char *tag;
BYTE by, subby;

 *ObjType=0;
 if(TAG.isEmpty()) ReadXMLTag(false);
 by = this->by;
 subby = this->subby;

 switch(by)
	{
	case XML_char:
		if(this->subby=='\n')
		  {ObjType[0]='\\'; ObjType[1]='n'; ObjType[2]=0;}
		else
		  {ObjType[0]=this->subby; ObjType[1]=0;}
		tag = ObjType;
		switch(this->subby)                        //Normal character
		  {
		  case 10:
		  case 13:strcpy(ObjType, "\\n");break;	//CR
		  case  9:strcpy(ObjType, "!Tab");break;
		  case 32:by=32;break;  //Space
		  }
	       break;
	case XML_extchar:
	       if(TAG.isEmpty()) break;			//Extended chatacter &xxx;
	       loc_TAG = Ext_chr_str(TAG[0],this)+copy(TAG,1,length(TAG)-1);
	       tag = loc_TAG();
	       //by=201;
	       break;

	case XML_badextchar:
               loc_TAG = copy(TAG,1,length(TAG)-2);   //Extended chatacter &xxx;
	       if((tag=loc_TAG())==NULL) break;
/*
	       if(TAG=="nbsp")	{by=200;break;}  	//Hard space

	       if((i=(TAG() IN HTMLChars))>0)
		  {
		  i--;
		  by=201;
		  tag = Ext_chr_str(i, cq, ConvertHTML); //Translate HTML character set to WP5.x one
		  } */
	       break;

	case XML_tag:
               loc_TAG = copy(TAG,1,length(TAG)-2);	//Normal tag <xxx>
	       if((tag=loc_TAG())==NULL) break;

	       //if(TAG=="?xml")    {MetaXML(this);break;}
	       if(loc_TAG=="rect")    {by=130;break;}
	       if(loc_TAG=="circle")  {by=131;break;}
	       if(loc_TAG=="ellipse") {by=132;break;}
	       if(loc_TAG=="line")    {by=133;break;}
	       if(loc_TAG=="polyline"){by=134;break;}
	       if(loc_TAG=="polygon") {by=135;break;}
	       if(loc_TAG=="path")    {by=136;break;}
	       if(loc_TAG=="g")       {by=137;break;}
	       if(loc_TAG=="text")    {by=138;break;}
	       break;
	case XML_closetag:
               loc_TAG = copy(TAG,2,length(TAG)-3);	//Closing tag </xxx>
	       if((tag=loc_TAG())==NULL) break;
	       break;
	case XML_comment: 			//comment
        case XML_CDATA:
	       break;
	}

  this->by = by;
  this->subby = subby;
  if(flag<Nothing)
    switch(by)
	{
/*	case XML_char:		//Normal character
               tag=Ext_chr_str(subby,cq,ConvertCpg);
	       CharacterStr(cq,tag);
	       break;		//Normal character */
        case XML_CDATA:
	case XML_comment:
               CommentXML();
	       break;
/*	case XML_unicode:
               CharacterStr(cq,TAG);
	       break;		//Already expanded unicode character */

	case 32:putc(' ', strip);   /*soft space*/
		break;

	case 130:Rectangle(); break;
	case 131:Circle(); break;
	case 132:Ellipse(); break;
	case 133:Line(); break;
	case 134:PolyLine(); break;
	case 135:Polygon(); break;
	case 136:Path(); break;
	case 137:Group(); break;
	case 138:Text(); break;
	}


 this->by = by;
 this->subby = subby;		// restore local by & subby.
 if (log != NULL)
    {   /**/
    if(by==128)
        fputc('\n',log);
    else if(by==' ' || by==200) fputc(' ',log);
    else if(by==0 || by==201)
	{
	fprintf(log,"%s",tag);
	}
    else
	{
	fprintf(log, _("\n%*s [%s %s]   "),
		  recursion * 2, "", TAG(), ObjType);
//	if(*ObjType==0) UnknownObjects++;
	}
    }

 ActualPos = ftell(wpd);
}


int TconvertedPass1_SVG::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Convert_first_pass() ");fflush(log);
#endif
DWORD fsize;
int RetVal = 0;

  PositionX = PositionY = 0;
  if(Verbosing >= 1)
     printf(_("\n>>>SVG2LaTeX<<< Conversion program: From SVG to LaTeX Version %s\n"
	      "      Made by J.Fojtik  (Hosted on WP2LaTeX :))))\n\n"),
			SVGVersion);
  ConvertHTML = GetTranslator("htmlTOinternal");
  CharReader = &ch_fgetc;

  TablePos=0;

  DocumentStart = ftell(wpd);
  fsize = FileSize(wpd);
  perc.Init(ftell(wpd), fsize,_("First pass SVG:") );

  ActualPos = ftell(wpd);
  while(ActualPos < fsize)
      {
      if(Verbosing >= 1)		//actualise a procentage counter
	      perc.Actualise(ActualPos);

      TAG.erase();
      ProcessKeySVG();
      }

  const float Scale = /*GetScale2PSU((TMapMode)MapMode) * */ 25.4f / 71.0f;	// convert PSu to WPGu (quite bad).

  if(VectList.VectorObjects>0)
  {
    vFlip flipTrx(bbx.MinY, bbx.MaxY);
    VectList.Transform(flipTrx);

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

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

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

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

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

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

    PutImageIncluder(NewFilename());

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

  //OutCodePage = CodePageBk;
  return(RetVal);
}

