/******************************************************************************
 * 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		                                              *
 ******************************************************************************/
// https://www.w3schools.com/graphics/svg_intro.asp
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#path_commands
// http://xahlee.info/js/svg_path_ellipse_arc.html
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

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

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

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

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

#include "wmfsupp.h"

#include "arith.h"


extern list HTMLChars;
int SavePictureEPS(const char *Name, const Image &Img);
void CheckOversizedImage(Image *PImg);
float NormAngleDEG(float num1);


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


class DefinedObject: public vecPen, public vecBrush
{
public:
    DefinedObject(const string &NewId): id(NewId), PropsSet(0) {};

    VectorList VectList;
    string id;
    unsigned char PropsSet;

    typedef enum
    {
      EpropFillColor = 1,
      EpropLineColor = 2
    }ePropList;
};


class ForwardHolderBase: public VectorObject
{
public:
    ForwardHolderBase(const char *Key): KeyHook(Key) {};
    //ForwardHolderBase(const string &KeyS): KeyHook(KeyS) {};

    virtual void DoTheJob(const DefinedObject *pDefDObj, VectorList &VectList) = 0;

    string KeyHook;
};


class ForwardHolder: public ForwardHolderBase
{
public:
    ForwardHolder(const char *Key, float NewX, float NewY, uint8_t newFeatures=0xFF): x(NewX), y(NewY), ForwardHolderBase(Key) {};
    //ForwardHolder(const string &KeyS, float NewX, float NewY, uint8_t newFeatures=0xFF): x(NewX), y(NewY), ForwardHolderBase(KeyS) {};

    virtual VectorObject *Duplicate(void) const;
    virtual void DoTheJob(const DefinedObject *pDefDObj, VectorList &VectList);

    float x,y;
};


VectorObject *ForwardHolder::Duplicate(void) const
{
  return new ForwardHolder(KeyHook(),x,y);
}


void ForwardHolder::DoTheJob(const DefinedObject *pDefDObj, VectorList &VectList)
{
  if(pDefDObj==NULL) return;
  VectorObject *Replacement = pDefDObj->VectList.Duplicate();
  if(this->x!=0 || this->y!=0)
  {
    TransformSVG *pTx = new TransformSVG;
    pTx->CTM(3,0) = this->x;
    pTx->CTM(3,1) = this->y;
    Replacement->Transform(*pTx);	// Apply shift x,y operation.
  }
  if(!VectList.ReplaceObject(this,Replacement))
          delete Replacement;		// Not found, should not occur.
  //delete pObj;			// pObj is either replaced or not found, erase it.
}


class ForwardHolderSolveFill: public ForwardHolderBase
{
public:
    ForwardHolderSolveFill(const char *Key, vecBrush *NewVB): ForwardHolderBase(Key), VB(NewVB)  {};

    virtual void DoTheJob(const DefinedObject *pDefDObj, VectorList &VectList);
    virtual VectorObject *Duplicate(void) const;

    vecBrush *VB;
};


VectorObject *ForwardHolderSolveFill::Duplicate(void) const
{
  return new ForwardHolderSolveFill(KeyHook(),VB);
}


void ForwardHolderSolveFill::DoTheJob(const DefinedObject *pDefDObj, VectorList &VectList)
{
 if(pDefDObj==NULL || VB==NULL) return;
 if((pDefDObj->PropsSet&DefinedObject::EpropFillColor) == 0) return;
 VB->FillColor = pDefDObj->FillColor;
 if(VB->BrushStyle==FILL_NONE) VB->BrushStyle=FILL_SOLID;
}


/// Read float value from in memory string.
/// @param[in,out]	pstr	Pointer to a string to 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!='-')
  {
    if(!strnicmp(str,"nan",3))
    {
      *pstr = str+3;
      MakeNaN(&f);
      return true;
    }
    *pstr = str;
    f = 0;
    return false;
  }
  f = atof(str);

  if(*str=='-') str++;
  while(isdigit(*str)) str++;
  if(*str=='.')		// Only one dot is tolerated inside number.
  {
    str++;
    while(isdigit(*str)) str++;
  }
  if(*str=='e')
  {
    str++;
    if(*str=='-' || *str=='+') str++;
    while(isdigit(*str)) str++;
    if(*str=='.')		// Only one dot is tolerated inside exponent.
    {
      str++;
      while(isdigit(*str)) str++;
    }
  }

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

  *pstr = str;
return true;
}


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


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


const char LINE_COLOR[] = "stroke";
const char FILL_COLOR[] = "fill";

class VectDictionaryItem
{
public:
    string Id;
    VectorList VectList;
};


typedef enum
{
	SVG_RECT = 130,
	SVG_CIRCLE = 131,
	SVG_ELIPSE = 132,
	SVG_LINE = 133,
	SVG_POLYLINE = 134,
	SVG_POLYGON = 135,
	SVG_PATH = 136,
	SVG_A,
	SVG_G,
	SVG_TEXT,
	SVG_DEFS,
	SVG_USE,
	SVG_IMAGE,
	SVG_STYLE
} E_SVG_TAGS;


/// Register translator object here.
class TconvertedPass1_SVG: virtual public TconvertedPass1_XML, virtual public TconvertedPass1_Image
     {
protected:
     typedef struct
     {
       float rx, ry, rotation, dx, dy;
       char large_arc, sweep;
     } SvgEllipse;

     VectorList VectList;			///< Main Vector object list.
     doublelist StyleDict;
     PS_State PSS;
     float PositionX, PositionY;

     DefinedObject **objs;
     int DObjectCount;
     ForwardHolderBase **DelayedObjects;		///< List of mostly empty objects that are simultaneosly stored in the main VectorObject list.
     int DelayedObjCount;
     CpTranslator *PsNativeCP;
     CpTranslator *PsNativeSym;

     //RGB_Record DefaultLineColor;
     //RGB_Record DefaultFillColor;
     //unsigned char DefaultLineStyle;
     //unsigned char DefaultBrushStyle;

     bool GetProp(const char *PropName, float &f);
     int GetColor(RGB_Record &LineColor, const char *ClrType=LINE_COLOR);
     void GetLineCap(unsigned char &LineCap);
     void GetLineJoin(unsigned char &LineJoin);
     void GetStyle(vecPen *pPen, vecBrush *pBrush);
     void GetStyle(vecPen *pPen, vecBrush *pBrush, const char * const Payload);
     float *LoadPoints(int &n);
     AbstractTransformXY *getTransform(void);
     void ClosePathLine(float **pPoints, int & n, bool Close=false, AbstractTransformXY *Tx=NULL);
     void ClosePathCurve(float **pPoints, int & n, bool Close=false, AbstractTransformXY *Tx=NULL);
     void SolveExtProperty(const char *PropName, vecPen *pVvecPen, vecBrush *pVvecBrush);
     bool SolveEllipse(const SvgEllipse &Ellip, double x1, double y1, double x2, double y2, AbstractTransformXY *Tx);
     bool FakeEllipse(float x2, float y2, AbstractTransformXY *Tx, char CharPassed);

public:
     TconvertedPass1_SVG(void);
     virtual ~TconvertedPass1_SVG(void) {EraseObjCache();}

     void AddDefine(DefinedObject *pObj);
     void EraseObjCache(void);
     DefinedObject *GetDefine(const char *DefId);
     void AddDelayedObject(ForwardHolderBase *pFwdObj);
     TextContainer *FlushText(float x, float & y, int LineLen, TextContainer *pTextCont);

     virtual int Convert_first_pass(void);
     void ProcessKeySVG(void);
     void ProcessImage(void);
     void SolveDelayedObjs(void);

     void Anchor(void);
     void Circle(void);
     void Defs(void);
     void Ellipse(void);
     void Group(void);
     void ImageEmbed(void);
     void Line(void);
     void Path(void);
     void Polygon(void);
     void PolyLine(void);
     void Rectangle(void);
     void Style(void);
     void Text(void);
     void Use(void);

     friend Image LoadPictureSVG(const char *Name);
     };
TconvertedPass1 *Factory_SVG(void) {return new TconvertedPass1_SVG;}
FFormatTranslator FormatSVG("SVG",Factory_SVG);

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

ImplementReader(SVG,".SVG")
   LoadPictureSVG,
   NULL);


#define SVGVersion "0.18"


TconvertedPass1_SVG::TconvertedPass1_SVG(void): DelayedObjects(NULL), objs(NULL) 
{
  PSS.LineStyle = 0;
  PSS.FillPattern = FILL_SOLID;		// Default fill is a solid black.
  DelayedObjCount = DObjectCount = 0;
  PsNativeCP = GetTranslator("internalTOcp1276");
  PsNativeSym = GetTranslator("internalTOsymbol");
}


void TconvertedPass1_SVG::EraseObjCache(void)
{
  if(objs)
  {
    while(DObjectCount>0)
    {
      DObjectCount--;
      if(objs[DObjectCount] != NULL)
      {
        delete objs[DObjectCount];
        objs[DObjectCount] = NULL;
      }
    }
    free(objs);
    objs = NULL;
  }
  if(DelayedObjects)
  {
	// Objects are double referenced, do not erase them from this list.
    DelayedObjCount = 0;
    free(DelayedObjects);
    DelayedObjects = NULL;
  }
}


void TconvertedPass1_SVG::AddDefine(DefinedObject *pObj)
{
  if(objs==NULL)
  {
    objs = (DefinedObject**)malloc(sizeof(DefinedObject*));
    if(objs==NULL)
    {
      delete pObj;
      return;
    }
    DObjectCount = 1;
  }
  else
  {
    DObjectCount++;
    void *NewObjs = realloc(objs, DObjectCount*sizeof(DefinedObject*));
    if(NewObjs==NULL)
    {
      DObjectCount--;
      delete pObj;
      return;
    }
    objs = (DefinedObject**)NewObjs;
  }
  objs[DObjectCount-1] = pObj;
}


void TconvertedPass1_SVG::AddDelayedObject(ForwardHolderBase *pFwdObj)
{
  if(DelayedObjects==NULL)
  {
    DelayedObjects = (ForwardHolderBase**)malloc(sizeof(ForwardHolderBase*));
    if(DelayedObjects==NULL)
    {
      //delete pObj;	//No delete
      return;
    }
    DelayedObjCount = 1;
  }
  else
  {
    DelayedObjCount++;
    void *NewObjs = realloc(DelayedObjects, DelayedObjCount*sizeof(ForwardHolder*));
    if(NewObjs==NULL)
    {
      DObjectCount--;
      //delete pObj;	//No delete
      return;
    }
    DelayedObjects = (ForwardHolderBase**)NewObjs;
  }
  DelayedObjects[DelayedObjCount-1] = pFwdObj;
}


DefinedObject *TconvertedPass1_SVG::GetDefine(const char *DefId)
{
  if(objs==NULL) return NULL;
  for(int i=0; i<DObjectCount; i++)
  {
    if(objs[i]==NULL) continue;
    if(objs[i]->id==DefId) return objs[i];
  }
  return NULL;
}


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


/// Extract a color from style string item.
/// @return	-1 on failure; 0 when no color specified; 1 Color is OK; 2 reference to a style.
int GetStyleColor(RGB_Record *pColor, const char *ClrText)
{
  if(ClrText==NULL) return -1;

  while(isspace(*ClrText)) ClrText++;
  if(!strncmp(ClrText,"rgb",3))
  {
    ClrText += 3;
    while(isspace(*ClrText)) ClrText++;
    if(*ClrText=='(')
    {
      ClrText++;
      pColor->Red = atoi(ClrText);
      while(*ClrText!=',' && *ClrText!=0) ClrText++;
      if(*ClrText==',')
      {
        ClrText++;
        pColor->Green = atoi(ClrText);
        while(*ClrText!=',' && *ClrText!=0) ClrText++;
        if(*ClrText==',')
        {
          ClrText++;
          pColor->Blue = atoi(ClrText);
          return 1;
        }
      }
    }
    return -1;
  }

  if(*ClrText=='#')
  {
    ClrText++;
    while(isspace(*ClrText)) ClrText++;
    if(*ClrText==0) return false;
    const unsigned number = strtoul(ClrText, NULL, 16);
    int letters = 0;
    while(isxdigit(*ClrText) && letters<4)
    {
      letters++;
      ClrText++;
    }
    if(letters<=3)
    {
      pColor->Blue = 0x11 * (number & 0xF);
      pColor->Green = 0x11 * ((number>>4) & 0xF);
      pColor->Red = 0x11 * ((number>>8) & 0xF);
    }
    else
    {
      pColor->Blue = number & 0xFF;
      pColor->Green = (number>>8) & 0xFF;
      pColor->Red = (number>>16) & 0xFF;
    }
    return 1;
  }

	// Look into a color dictionary.
  string Color;
  while(isalpha(*ClrText))
  {
    Color += *ClrText++;
    if(Color.length()>16) return false;		// maximal color string length
  }
  if(Color.length() > 0)
  {
    if(Color=="none") return 0;
    for(int i=0; i<sizeof(SVG_colors)/sizeof(SVG_color);i++)
      if(!strcmp(Color(), SVG_colors[i].desc))
      {
        *pColor = SVG_colors[i].color;
        return 1;
      }
  }

  return -1;
}


/// Decode color.
/// @return -1 on failure; 0 when no color specified; 1 Color is OK; 2 reference to a style.
int TconvertedPass1_SVG::GetColor(RGB_Record &LineColor, const char *ClrType)
{
int i;

  if(ClrType==NULL || *ClrType==0) return -1;
  if((i=TAG_Args IN ClrType)<0) return -1;
  const char * const Payload = TAG_Args.Member(i,1);
  if(Payload==NULL) return -1;
 
  if(!strncmp(Payload,"url(#",5)) return 2;

  return(GetStyleColor(&LineColor,Payload));
}


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


void TconvertedPass1_SVG::GetStyle(vecPen *pPen, vecBrush *pBrush, const char * const Payload)
{
const char *str;
  if(Payload==NULL) return;
  if(pPen)
  {
    str = strstr(Payload,"stroke-width:");
    if(str)
    {
      str += 13;
      pPen->PenWidth = atof(str);
    }
    str = strstr(Payload,"stroke:");
    if(str)
    {
      str += 7;
      if(GetStyleColor(&pPen->LineColor,str)>0)
          pPen->LineStyle = 1;
    }
  }
  if(pBrush)
  {
    str = strstr(Payload,"fill:");
    if(str)
    {
      str += 5;
      switch(GetStyleColor(&pBrush->FillColor,str))
      {
        case 0: pBrush->BrushStyle=FILL_NONE; break;
        case 1: pBrush->BrushStyle=FILL_SOLID; break;
        default: break;
      }
    }
  }
}


void TconvertedPass1_SVG::GetStyle(vecPen *pPen, vecBrush *pBrush)
{
int i;

/*
  if(pPen)
  {
    pPen->LineStyle = PSS.LineStyle;	// DefaultLineStyle; // Default LineStyle is none.
    memcpy(&pPen->LineColor, &DefaultLineColor, sizeof(DefaultLineColor));
  }
  if(pBrush)
  {
    pBrush->BrushStyle = DefaultBrushStyle;	// Default LineStyle is none.
    memcpy(&pBrush->FillColor, &DefaultFillColor, sizeof(DefaultFillColor));
  }
*/

  if((i=TAG_Args IN "style")>=0)
    GetStyle(pPen, pBrush, TAG_Args.Member(i,1));

  if((i=TAG_Args IN "class")>=0)
  {
    const char * Payload = TAG_Args.Member(i,1);
    if(Payload!=NULL)
    {
      GetStyle(pPen, pBrush, StyleDict.Find(Payload));
    }
  }
}


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]));
      if(StrPoints[i]=='.')
      {
        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]));
      if(StrPoints[i]=='.')
      {
        do
        { i++;}
        while(isdigit(StrPoints[i]));
      }
      continue;
    }
    i++;
  }

  n/=2;
  for(i=0; 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++;
        }
        while(isspace(*StrTransform)) StrTransform++;
        if(*StrTransform==')')		// only one argument for 2 directions
        {
          Tx(3,1) = Tx(3,0);
          pTransformSVG->CTM *= Tx;
        }
        else if(ReadFloat(&StrTransform, Tx(3,1)))
        {
          pTransformSVG->CTM *= Tx;
        }
        else
        {
          if(err != NULL)
          {
            perc.Hide();
            fprintf(err,_("\nError: Unexpected content in translate transform command: \"%s\"."),StrTransform);
          }
        }
      }
    } 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++;
          }
          while(isspace(*StrTransform)) StrTransform++;
          if(*StrTransform==')')		// only one argument for 2 directions
          {
            Tx(1,1) = Tx(0,0);
            pTransformSVG->CTM *= Tx;
          }
          else if(ReadFloat(&StrTransform, Tx(1,1)))	// different scales in both spatial directions
          {
            pTransformSVG->CTM *= Tx;
          }
          else
          {
            if(err != NULL)
            {
              perc.Hide();
              fprintf(err,_("\nError: Unexpected content in translate scale command: \"%s\"."),StrTransform);
            }
          }
        }
      }
    } 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));
      if(*StrTransform==',') StrTransform++;
      Result &= ReadFloat(&StrTransform, Tx(0,1));

      if(*StrTransform==',') StrTransform++;
      Result &= ReadFloat(&StrTransform, Tx(1,0));
      if(*StrTransform==',') StrTransform++;
      Result &= ReadFloat(&StrTransform, Tx(1,1));

      if(*StrTransform==',') StrTransform++;
      Result &= ReadFloat(&StrTransform, Tx(3,0));
      if(*StrTransform==',') StrTransform++;
      Result &= ReadFloat(&StrTransform, Tx(3,1));

#ifdef _DEBUG
/*
      for(int y=0; y<4; y++)
        for(int x=0; x<4; x++) fprintf(log," %f",Tx(x,y));
      fprintf(log,"\n");
*/
#endif

      if(Result)
      {
        pTransformSVG->CTM *= Tx;
      }
    } else
    {			// No keyword recognised; skip one character.
      fprintf(err,_("\nError: unrecognised SVG transform keyword: \"%s\"."), StrTransform);
      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::Circle ");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))
  {
    //const float Scale = GetScale2PSU((TMapMode)MapMode);

    VectorEllipse *pVectCirc = new VectorEllipse(cy-r, cy+r, cx+r, cx-r);
    pVectCirc->AttribFromPSS(PSS);
    GetStyle(pVectCirc,pVectCirc);
    if(GetProp("stroke-width",stroke_width))
    {
      pVectCirc->PenWidth = stroke_width;
    }
    GetColor(pVectCirc->LineColor);
    GetLineCap(pVectCirc->LineCap);
    GetLineJoin(pVectCirc->LineJoin);
    switch(GetColor(pVectCirc->FillColor,FILL_COLOR))
    {
      case 0: pVectCirc->BrushStyle=FILL_NONE;
              break;
      case 1: if(pVectCirc->BrushStyle==FILL_NONE) pVectCirc->BrushStyle=FILL_SOLID;
              break;    
      //default:
    }

    UpdateBBox(bbx, 0, cx-r-pVectCirc->PenWidth/2, cy-r-pVectCirc->PenWidth/2, 2*r+pVectCirc->PenWidth, 2*r+pVectCirc->PenWidth);
    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;

  {
    VectorEllipse *pVectEll = new VectorEllipse(cy-ry, cy+ry, cx+rx, cx-rx);
    pVectEll->AttribFromPSS(PSS);
    GetStyle(pVectEll,pVectEll);
    if(GetProp("stroke-width",stroke_width))
    {
      pVectEll->PenWidth = stroke_width;
    }
    GetColor(pVectEll->LineColor);
    GetLineCap(pVectEll->LineCap);
    GetLineJoin(pVectEll->LineJoin);

    UpdateBBox(bbx, 0, cx-rx-pVectEll->PenWidth/2, cy-ry-pVectEll->PenWidth/2, 2*rx+pVectEll->PenWidth, 2*ry+pVectEll->PenWidth);
    VectList.AddObject(pVectEll);
  }
  strcpy(ObjType,ObjName+1);
  return;

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


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

  GetLineCap(PSS.LineCap);
  GetLineJoin(PSS.LineJoin);
  GetColor(PSS.LineColor);
  GetColor(PSS.FillColor,FILL_COLOR);
  AbstractTransformXY *pTRx = getTransform();
  if(!GetProp("font-size",PSS.FontSize))
    PSS.FontSize = FontSizeBk;
  else
    PSS.FontSize /= 2.66;		// [pt] --> [mm]

  InitBBox(bbx);
  VectList.Swap(VectListIn);
  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</a>")
        break;
    ProcessKeySVG();
  }
  recursion--;
  VectList.Swap(VectListIn);

  if(VectListIn.VectorObjects > 0)
  {		// Bounding box is in PS units.
    if(pTRx != NULL)
    {
#ifdef _DEBUG
/*
      if(bbx.MaxX>=bbx.MinX && bbx.MaxY>=bbx.MinY)
      {
        VectorRectangle *pVR=new VectorRectangle(bbx.MinY, bbx.MaxY, bbx.MinX, bbx.MaxX);
        pVR->LineColor.Red = 128;
        pVR->LineColor.Green = 128;
        pVR->BrushStyle = 1;
        VectListIn.AddObject(pVR);
      }
*/
#endif
      VectListIn.Transform(*pTRx);
    }
    VectList.Append(VectListIn);
  }
if(bbx.MaxX>=bbx.MinX && bbx.MaxY>=bbx.MinY)
        UpdateBBox(bbxBk,pTRx, bbx);
  bbx = bbxBk;

  if(pTRx != NULL) {delete(pTRx); pTRx=NULL;}

  PSS.LineCap = LineCapBK;
  PSS.LineJoin = LineJoinBK;
  PSS.LineColor = LineColorBK;
  PSS.FontSize = FontSizeBk;
  PSS.FillColor = FillColorBK;

strcpy(ObjType,"A");
}


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

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

  if(!GetProp("font-size",PSS.FontSize))
    PSS.FontSize = FontSizeBk;
  else
    PSS.FontSize /= 2.66;		// [pt] --> [mm]

  InitBBox(bbx);
  VectList.Swap(VectListIn);
  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</g>")
        break;
    ProcessKeySVG();
  }
  recursion--;
  VectList.Swap(VectListIn);

  if(VectListIn.VectorObjects > 0)
  {		// Bounding box is in PS units.
    if(pTRx != NULL)
    {
#ifdef _DEBUG

      if(bbx.MaxX>=bbx.MinX && bbx.MaxY>=bbx.MinY)
      {
        VectorRectangle *pVR=new VectorRectangle(bbx.MinY, bbx.MaxY, bbx.MinX, bbx.MaxX);
        pVR->LineColor.Red = 128;
        pVR->LineColor.Green = 128;
        pVR->BrushStyle = 0;
        VectListIn.AddObject(pVR);
      }

#endif
      VectListIn.Transform(*pTRx);
    }
    VectList.Append(VectListIn);
  }
if(bbx.MaxX>=bbx.MinX && bbx.MaxY>=bbx.MinY)
        UpdateBBox(bbxBk,pTRx, bbx);
  bbx = bbxBk;

  if(pTRx != NULL) {delete(pTRx); pTRx=NULL;}

  PSS.LineCap = LineCapBK;
  PSS.LineJoin = LineJoinBK;
  PSS.LineColor = LineColorBK;
  PSS.FontSize = FontSizeBk;
  PSS.FillColor = FillColorBK;

strcpy(ObjType,"Group");
}


void TconvertedPass1_SVG::Style(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Style ");fflush(log);
#endif
char status = 0;
string key, commands;

  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</style>")
        break;
    if(by==XML_char)
    {
      switch(status)
      {
        case 0: if(subby=='.')
                {
                  key.erase();
                  commands.erase();
                  status = 1;
                  break;
                }
        case 1: if(subby=='{')
                {
                  status = 2;
                  break;
                }
                if(key.length() > 1024)
                {
                  if(err != NULL)
                  {
                    perc.Hide();
                    fprintf(err,_("\nError: Style key is too big: \"%s\"."),key());
                  }
                  status = 127;
                  break;
                }
                key += subby;
                break;
        case 2: if(subby=='}')
                {
                  StyleDict.Add(key(), commands());
                  status = 0;
                  break;
                }
                if(commands.length() > 4096)
                {
                  if(err != NULL)
                  {
                    perc.Hide();
                    fprintf(err,_("\nError: Style contents is too big: \"%s\"."),commands());
                  }
                  status = 127;
                  break;
                }
                commands += subby;
                break;
        case 127: break;
     }
      continue;
    }
    ProcessKeySVG();
  }
  recursion--;

  strcpy(ObjType,"Style");
}



TextContainer *TconvertedPass1_SVG::FlushText(float x, float & y, int LineLen, TextContainer *pTextCont)
{
  if(pTextCont==NULL) return NULL;
  if(pTextCont->isEmpty()) return pTextCont;
  
  pTextCont->PosX = x;
  pTextCont->PosY = y;
  //pTextCont->AddText(temp_string(txt),PSS);
  VectList.AddObject(pTextCont);
  float FontSize = (PSS.FontSize <= 0) ? 0.25f : PSS.FontSize;
  UpdateBBox(bbx,0, x,y, LineLen*mm2PSu(FontSize), -1.1f*mm2PSu(FontSize));
  y += 1.1f*mm2PSu(FontSize);
return new TextContainer;
}


void TconvertedPass1_SVG::Text(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Text ");fflush(log);
#endif
float x, y;
int LineLen = 0;
int i;
const RGB_Record BkTextColor = PSS.TextColor;
const float FontSizeBk = PSS.FontSize;
bool SpaceAdded = false;

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

  if((i=TAG_Args IN "style")>=0)
  {
    const char *arg = TAG_Args.Member(i,1);
    if(arg)
    {
      const char *str = strstr(arg,"font-size:");
      if(str)
      {
        str += 10;
        PSS.FontSize = atof(str)/2.66;		// [pt] --> [mm]
      }
      str = strstr(arg,"fill:");
      if(str)
      {
        GetStyleColor(&PSS.TextColor,str+5);
      }
      //GetStyle(pPen, pBrush, TAG_Args.Member(i,1));
    }
  }

  GetColor(PSS.TextColor,FILL_COLOR);
  if(GetProp("font-size",PSS.FontSize))
  {
    PSS.FontSize /= 2.66;		// [pt] --> [mm]
  }

  recursion++;
  TextContainer *pTextCont = new TextContainer;  
  while(!feof(wpd))
  {
    ReadXMLTag(false);
    if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</text>")
        break;

    if(by==XML_tag && TAG=="<tspan>")
    {
      if(!pTextCont->isEmpty())
      {
        pTextCont = FlushText(x,y,LineLen,pTextCont);
        LineLen = 0;
      }

      recursion++;
      const float FontSizeBkSpan = PSS.FontSize;
      const RGB_Record TextColorBk = PSS.TextColor;
      const float BkX = x;
      const float BkY = y;

      GetProp("x",x);
      GetProp("y",y);
      if((i=TAG_Args IN "style")>=0)
      {
        const char *arg = TAG_Args.Member(i,1);
        if(arg)
        {
          const char *str = strstr(arg,"font-size:");
          if(str)
          {
            str += 10;
            PSS.FontSize = atof(str)/2.66;		// [pt] --> [mm]
          }
          str = strstr(arg,"fill:");
          if(str)
          {
            GetStyleColor(&PSS.TextColor,str+5);
          }
          //GetStyle(pPen, pBrush, TAG_Args.Member(i,1));
        }
      }
      GetColor(PSS.TextColor,FILL_COLOR);
      if(GetProp("font-size",PSS.FontSize))
      {
        PSS.FontSize /= 2.66;		// [pt] --> [mm]
      }

      while(!feof(wpd))
      {
        ReadXMLTag(false);
        if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</tspan>")
            break;
        switch(by)
        {
          case XML_char:
              if(log!=NULL) fputc(subby,log);
              if(subby=='\r' || subby=='\n')
              {
                if(!pTextCont->isEmpty()) continue;
                if(!SpaceAdded)
                  AddCharacterToContainer(pTextCont, ' ', PsNativeCP, PsNativeSym, this, PSS);
                SpaceAdded = true;
                continue;
              }
              SpaceAdded = subby==' ';
              AddCharacterToContainer(pTextCont, subby, PsNativeCP, PsNativeSym, this, PSS);
              LineLen++;
              continue;

          case XML_extchar:
	      if(!TAG.isEmpty())
              {
                if(log!=NULL) fputs(TAG(),log);
                if(TAG[0]=='&' && TAG[TAG.length()-1]==';')
                  TAG = copy(TAG,1,TAG.length()-2);
                int i = (TAG() IN HTMLChars);
                if(i>0 && ConvertHTML!=NULL)
		{
                  const uint16_t WchI = (*ConvertHTML)[i-1];
                  AddCharacterToContainer(pTextCont, WchI, PsNativeCP, PsNativeSym, this, PSS);
                  SpaceAdded = false;
                  LineLen++;
                }
              }
              continue;
        }    
        ProcessKeySVG();
      }

      if(!pTextCont->isEmpty())
      {
        pTextCont = FlushText(x,y,LineLen,pTextCont);
        LineLen = 0;
      }

      PSS.FontSize = FontSizeBkSpan;
      PSS.TextColor = TextColorBk;
      x = BkX;
      y = BkY;
      recursion--;
    }

    switch(by)
    {
      case XML_char:
          if(subby=='\r' || subby=='\n')
          {
            if(!pTextCont->isEmpty()) continue;
            if(!SpaceAdded)
                AddCharacterToContainer(pTextCont, ' ', PsNativeCP, PsNativeSym, this, PSS);
            SpaceAdded = true;
            continue;
          }
          AddCharacterToContainer(pTextCont, subby, PsNativeCP, PsNativeSym, this, PSS);
          SpaceAdded = subby==' ';
          LineLen++;
          continue;

      case XML_extchar:
          if(!TAG.isEmpty())
	  {
            if(log!=NULL) fputs(TAG(),log);
            if(TAG[0]=='&' && TAG[TAG.length()-1]==';')
                TAG = copy(TAG,1,TAG.length()-2);
            int i = (TAG() IN HTMLChars);
            if(i>0 && ConvertHTML!=NULL)
	    {
              const uint16_t WchI = (*ConvertHTML)[i-1];
              AddCharacterToContainer(pTextCont, WchI, PsNativeCP, PsNativeSym, this, PSS);
              SpaceAdded = false;
              LineLen++;
            }
          }
          continue;
    }
    ProcessKeySVG();
  }
  recursion--;

  //if(log!=NULL && !txt.isEmpty())
  //    fputs(txt(),log);
  //txt = replacesubstring(txt,"\n"," ");
  //txt = replacesubstring(txt,"\r"," ");
  //LineLen = txt.length();

  if(!pTextCont->isEmpty())
  {
    pTextCont = FlushText(x,y,LineLen,pTextCont);
  }
  delete pTextCont;

  PSS.TextColor = BkTextColor;
  PSS.FontSize = FontSizeBk;
strcpy(ObjType,"Text");
}


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;
  
  {
    UpdateBBox(bbx,0, Points[0], Points[1], Points[2]-Points[0], Points[3]-Points[1]);

    VectorLine *pVecLine = new VectorLine(Points, 2); Points=NULL;
    pVecLine->AttribFromPSS(PSS);
    GetStyle(pVecLine,NULL);
    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, bool Close, AbstractTransformXY *Tx)
{
int i;
float stroke_width;
RGB_Record FillColor;

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

  PositionX = (*pPoints)[2*n-2];
  PositionY = (*pPoints)[2*n-1];
  for(i=0; i<n; i++)
  {
    if(Tx) Tx->ApplyTransform((*pPoints)[2*i], (*pPoints)[2*i+1]);
    UpdateBBox(bbx,0, (*pPoints)[2*i], (*pPoints)[2*i+1], 0, 0);
  }

  VectorLine *pVecLine;
  if(GetColor(FillColor,FILL_COLOR) > 0)
  {
     VectorPolygon *pVecPoly = new VectorPolygon(*pPoints,n);
     //pVecPoly->AttribFromPSS(PSS);
     pVecPoly->FillColor = FillColor;
     //pVecPoly->Outline = true;
     pVecPoly->AttribFromPSS(PSS);
     GetStyle(pVecPoly,NULL);
     if(pVecPoly->BrushStyle==0) pVecPoly->BrushStyle=1;
     pVecLine = pVecPoly;
  }
  else
  {
    pVecLine = new VectorLine(*pPoints,n);
    pVecLine->AttribFromPSS(PSS);
    GetStyle(pVecLine,NULL);
    if(pVecLine->LineStyle==0) pVecLine->LineStyle=1;
  }

  n = 0;
  *pPoints = NULL;

  if(GetProp("stroke-width",stroke_width))
  {
    pVecLine->PenWidth = stroke_width;
  }
  GetColor(pVecLine->LineColor);
  GetLineCap(pVecLine->LineCap);
  GetLineJoin(pVecLine->LineJoin);
  pVecLine->Close = Close;

  VectList.AddObject(pVecLine);
}


void TconvertedPass1_SVG::ClosePathCurve(float **pPoints, int & n, bool Close, AbstractTransformXY *Tx)
{
int i;
float stroke_width;
  if(*pPoints==NULL) return;
  if(n<=0)
  {
    free(*pPoints); *pPoints=NULL;
    return;
  }

  for(i=0; i<n; i++)
  {
    if(Tx)
    {
      Tx->ApplyTransform((*pPoints)[6*i], (*pPoints)[6*i+1]);
      Tx->ApplyTransform((*pPoints)[6*i+2], (*pPoints)[6*i+3]);
      Tx->ApplyTransform((*pPoints)[6*i+4], (*pPoints)[6*i+5]);
    }
    UpdateBBox(bbx,0, (*pPoints)[6*i],   (*pPoints)[6*i+1], 0, 0);	// start point    
  }
  PositionX = (*pPoints)[6*n];
  PositionY = (*pPoints)[6*n+1];
  if(Tx) Tx->ApplyTransform((*pPoints)[6*n], (*pPoints)[6*n+1]);	// The last curve point must be also transformed.
  UpdateBBox(bbx,0, (*pPoints)[6*n], (*pPoints)[6*n+1], 0, 0);		// curve end point

  VectorCurve *pVecCurve = new VectorCurve(*pPoints, 3*n+1); n=0;
  *pPoints = NULL;
  pVecCurve->AttribFromPSS(PSS);	// PSS contains default values
  GetStyle(pVecCurve,NULL);		// Get style values if exists.
  if(GetProp("stroke-width",stroke_width))
  {
    pVecCurve->PenWidth = stroke_width;
  }
  GetColor(pVecCurve->LineColor);
  GetLineCap(pVecCurve->LineCap);
  GetLineJoin(pVecCurve->LineJoin);
  switch(GetColor(pVecCurve->FillColor,FILL_COLOR))
  {
    case 0: pVecCurve->BrushStyle=FILL_NONE;
            break;
    case 1: if(pVecCurve->BrushStyle==FILL_NONE) pVecCurve->BrushStyle=FILL_SOLID;
	    //pVecCurve->Filled = true;
            break;
    //default:
  }

  pVecCurve->Close = Close;
  VectList.AddObject(pVecCurve);
}


/// <image id="image1JPEG" x="240" y="0" width="240" height="150" xlink:href="data:image/jpg;base64,
void TconvertedPass1_SVG::ImageEmbed(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ImageEmbed() ");fflush(log);
#endif
int i;
string NewFileName;
float x, y, Width, Height;

//  initBox(Box);
//  Box.Width = -1; 		// Undefined, use default 100mm
//  Box.Image_type=0;		// Image on disk
//  Box.AnchorType = 0; 		// 0-Paragraph, 1-Page, 2-Character
//  Box.HorizontalPos=2;		// 0-Left, 1-Right, 2-Center, 3-Full

  if(!GetProp("x",x)) x=0;
  if(!GetProp("y",y)) y=0;
  if(!GetProp("width",Width)) Width=10;
  if(!GetProp("height",Height)) Height=0;

  if((i=TAG_Args IN "src")>=0 || (i=TAG_Args IN "SRC")>=0)
  {
    const char *FileName = TAG_Args.Member(i,1);
    if(FileName!=NULL)
    {
      for(i=0; i<sizeof(MimeList)/sizeof(TMimeItem); i++)
      {
        if(!strncmp(FileName,MimeList[i].MimeType,MimeList[i].TypeLen))
        {
          NewFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(MimeList[i].ImageExt);
          FileName += MimeList[i].TypeLen + 1;
          break;
        }
      }

      if(NewFileName.length()>0)
      {
        FILE *F = fopen(NewFileName(),"wb");
        if(F)
        {
          base64_decode(FileName, F);
          fclose(F);
        }
        else
        {
          if(err != NULL)
          {
            perc.Hide();
            fprintf(err,_("\nError: Cannot create image file: \"%s\"."),NewFileName());
          }
        }
        //FileName = NewFileName();
      }
    }
  }

  if((i=TAG_Args IN "xlink:href") >= 0)
  {
    char *Mime = TAG_Args.Member(i,1);
    char *Contents = Mime;
    if(Contents != NULL)
    {
      Contents = strchr(Mime,',');
      if(Contents!=NULL)
      {
        if(*Contents==',') *Contents = 0;
        Contents++;
        //if(strncmp(Mime,"data:",5) Mime+=5;
        for(i=0; i<sizeof(MimeList)/sizeof(TMimeItem); i++)
        {
          if(!strncmp(Mime,MimeList[i].MimeType,MimeList[i].TypeLen))
          {
            NewFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(MimeList[i].ImageExt);
            //FileName += MimeList[i].TypeLen + 1;
            break;
          }
        }
      }
      else
      {
        Contents = Mime;
        Mime = NULL;
      }
    }
    if(Contents != NULL)
    {
      if(NewFileName.isEmpty())
      {
        NewFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(".JPG");
      }
      if(!NewFileName.isEmpty())
      {
        FILE *F = fopen(NewFileName(),"wb");
        if(F)
        {
          base64_decode(Contents, F);
          fclose(F);
          //FileName = NewFileName();
        }
        else
        {
          if(err != NULL)
          {
            perc.Hide();
	    fprintf(err,_("\nError: Cannot create image file: \"%s\"."),NewFileName());
          }
        }
      }
    }
  }

  if((i=TAG_Args IN "href") >= 0)
  {
    const char *Contents = TAG_Args.Member(i,1);
    if(Contents!=NULL && !strncmp(Contents,"data:",5))
    {
      for(i=0; i<sizeof(MimeList)/sizeof(TMimeItem); i++)
      {
        if(!strncmp(Contents,MimeList[i].MimeType,MimeList[i].TypeLen))
        {
          Contents += MimeList[i].TypeLen;
          if(*Contents==',') Contents++;
          NewFileName = MergePaths(OutputDir,RelativeFigDir) + GetSomeImgName(MimeList[i].ImageExt);
          FILE *F=fopen(NewFileName(),"wb");
          if(F==NULL) {NewFileName.erase();break;}
          base64_decode(Contents, F);
          fclose(F);
          break;
        }
      }
    }
  }

  if(!NewFileName.isEmpty())
  {
    Image Img2(LoadPicture(NewFileName()));
    if(!Img2.isEmpty() && Img2.Raster!=NULL)
    {
      VectorRaster *VecR = new VectorRaster(y, y+Height, x+Width, x);
      VecR->AttachRaster(Img2.Raster);
      VecR->AttachPalette(Img2.Palette);
      VecR->CalcBoundingBox(bbx);
      VectList.AddObject(VecR);
    }
    else
    {
      if(err != NULL)
      {
        perc.Hide();
        fprintf(err,_("\nError: Cannot load image file: \"%s\"."),NewFileName());
      }
    }
  }

  if(NewFileName.length()>0 && SaveWPG<0)
  {
    unlink(NewFileName());
  }

return;
}


float SolveQuadraticPos(const float anch, const float s)
{
  return (s-anch) / sqrtf(2.0) + anch;
}


void TconvertedPass1_SVG::SolveExtProperty(const char *PropName, vecPen *pVvecPen, vecBrush *pVvecBrush)
{
  if((pVvecPen==NULL&&pVvecBrush==NULL) || PropName==NULL) return;

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

  if(strncmp(Payload,"url(#",5)) return;
  Payload += 5;
  i = strlen(Payload) - 1;
  while(i>0)
  {
    if(Payload[i]==')')
    {
      string str(Payload,i);
      DefinedObject *DFO = GetDefine(str());
      if(DFO)
      {
        if(PropName==FILL_COLOR && pVvecBrush!=NULL &&
           (DFO->PropsSet&DefinedObject::EpropFillColor)==DefinedObject::EpropFillColor)
        {
          pVvecBrush->FillColor = DFO->FillColor;
          if(pVvecBrush->BrushStyle==FILL_NONE) pVvecBrush->BrushStyle=FILL_SOLID;
        }
      }
      else
      {
        if(PropName==FILL_COLOR && pVvecBrush!=NULL)
        {
          ForwardHolderSolveFill *pFwdObj = new ForwardHolderSolveFill(str, pVvecBrush);
          AddDelayedObject(pFwdObj);
        }
        return;
      }
      break;
    }
  }  
}


/// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
/// param[in]	Ellip	Shape and parametters of ellipsis.
/// param[in]	x1	Ellipse start point x coordinate.
/// param[in]	y1	Ellipse start point y coordinate.
bool TconvertedPass1_SVG::SolveEllipse(const SvgEllipse &Ellip, double x1, double y1, double x2, double y2, AbstractTransformXY *Tx)
{
const double a2 = Ellip.rx * Ellip.rx;
const double b2 = Ellip.ry * Ellip.ry;
const double cos_fi = cos(M_PI*Ellip.rotation/180);
const double sin_fi = sin(M_PI*Ellip.rotation/180);
const double xMid = (x1-x2) / 2;
const double yMid = (y1-y2) / 2;

  if(fabs(xMid)<1e-3 && fabs(yMid)<1e-3)	// Zero size arc - ignorred.
  {
    return true;
  }

  if(fabs(Ellip.rx)<1e-3 || fabs(Ellip.ry)<1e-3) return false;

	// Step 1: Compute (x1', y1')
  const double x1c =  cos_fi*xMid + sin_fi*yMid;
  const double y1c = -sin_fi*xMid + cos_fi*yMid;

	// Step 2: Compute (cx', cy')
  double d = (a2*b2 - a2*y1c*y1c - b2*x1c*x1c) / (a2*y1c*y1c + b2*x1c*x1c);
  if(d<0) return false;
  d = sqrt(d);
  if(Ellip.sweep==Ellip.large_arc) d=-d;

  const double cxc =  d * y1c * Ellip.rx/Ellip.ry; 
  const double cyc = -d * x1c * Ellip.ry/Ellip.rx;

	// Step 3: Compute (cx, cy) from (cx', cy')
  const double cx = cos_fi*cxc - sin_fi*cyc + (x1+x2)/2;
  const double cy = sin_fi*cxc + cos_fi*cyc + (y1+y2)/2;

	// Step 4
  const float fi1 = atan2(y1-cy, x1-cx);
  const float fi2 = atan2(y2-cy, x2-cx);

  float fi = fi1;
  int i = 0;
  while(fabs(NormAngleDEG(180*(fi-fi2)/M_PI)) > 11)
  {
    i++;
    if(Ellip.sweep)
      fi += 10*M_PI/180;
    else
      fi -= 10*M_PI/180;
  }
  if(i==0) return false;
  
  float *Points = (float*)malloc(2*(i+1)*sizeof(float));
  if(!Points) return false;

  Points[0]=x1;		Points[1]=y1;
  fi = fi1 - M_PI*Ellip.rotation/180;
  for(int j=1; j<i; j++)
  {
    if(Ellip.sweep)
      fi += 10*M_PI/180;
    else
      fi -= 10*M_PI/180;

    const float xR = Ellip.rx*cos(fi);
    const float yR = Ellip.ry*sin(fi);
  
    Points[2*j] =   cx + cos_fi*xR - sin_fi*yR;
    Points[2*j+1] = cy + sin_fi*xR + cos_fi*yR;
  }
  Points[2*i]=x2;	Points[2*i+1]=y2;
  i++;
  ClosePathLine(&Points,i,false,Tx);

 return true;
}


/// When elliptic arc cannot be constructed, use plain line segment instead.
bool TconvertedPass1_SVG::FakeEllipse(float x2, float y2, AbstractTransformXY *Tx, char CharPassed)
{
  if(fabs(PositionX-x2)<1e-5 && fabs(PositionY-y2)<1e-5) return true;
  float *Points = (float*)malloc(4*sizeof(float));
  if(!Points)
  {
    PositionX=x2; PositionY=y2;
    return false;
  }
  Points[0]=PositionX;	Points[1]=PositionY;
  Points[2]=x2;		Points[3]=y2;
  int n = 2;
  ClosePathLine(&Points,n,false,Tx);	// This sets PositionX and PositionY to [x2,y2]

  if(err!=NULL)
  {
    perc.Hide();
    fprintf(err,_("\nError: Path command '%c' is only partially supported!"),CharPassed);
  }
return true;
}


// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
void TconvertedPass1_SVG::Path(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Path ");fflush(log);
#endif
static const char ObjName[] = "!Path";
float *Points = NULL;
float val;
int n, i;
const char *StrMovements;
bool isCurve = false;
bool FirstCommand = true;
char LastCommand = 0;
AbstractTransformXY *Tx = NULL;
int SegmentCount = 0;
VectorList VectListIn;

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

  Tx = getTransform();
  VectListIn.Swap(VectList);
  while(*StrMovements != 0)
  {
    char ch = *StrMovements;
    if(ch==0) break;
    if(isspace(ch)) {StrMovements++;continue;}
    if(ch==',') {StrMovements++;continue;}	// Need to gobble whitespaces after comma.

    if(!(isdigit(ch) || ch=='.' || ch=='-'))
        LastCommand = *StrMovements++;
    switch(LastCommand)
    {
      case 'A':		// A rx ry x-axis-rotation large-arc-flag sweep-flag x y  see https://www.nan.fyi/svg-paths/arcs
        if(n>0) SegmentCount++;
        if(isCurve)
          ClosePathCurve(&Points,n,false,Tx);
        else
          ClosePathLine(&Points,n,false,Tx);
        {
          SvgEllipse Ellip;          
          ReadFloat(&StrMovements, Ellip.rx);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.ry);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.rotation);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, val);
	  Ellip.large_arc = (val==0) ? 0 : 1;
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, val);          
	  Ellip.sweep = (val==0) ? 0 : 1;
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.dx);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.dy);

          if(!SolveEllipse(Ellip, PositionX,PositionY, Ellip.dx,Ellip.dy, Tx))
          {
            if(!FakeEllipse(Ellip.dx,Ellip.dy,Tx,'A')) goto ExitErr;
          }
        }
	break;

      case 'a':		// A rx ry x-axis-rotation large-arc-flag sweep-flag dx dy  see https://www.nan.fyi/svg-paths/arcs
        if(n>0) SegmentCount++;
        if(isCurve)
          ClosePathCurve(&Points,n,false,Tx);
        else
          ClosePathLine(&Points,n,false,Tx);
        {
          SvgEllipse Ellip;          
          ReadFloat(&StrMovements, Ellip.rx);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.ry);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.rotation);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, val);
	  Ellip.large_arc = (val==0) ? 0 : 1;
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, val);
	  Ellip.sweep = (val==0) ? 0 : 1;
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.dx);
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, Ellip.dy);

          if(!SolveEllipse(Ellip, PositionX,PositionY, PositionX+Ellip.dx,PositionY+Ellip.dy, Tx))
          {
            if(!FakeEllipse(PositionX+Ellip.dx,PositionY+Ellip.dy,Tx,'a')) goto ExitErr;
          }          
        }
	break;

      case 'M':			//move to.
        if(n>0) SegmentCount++;
        if(isCurve)
          ClosePathCurve(&Points,n,false,Tx);
        else
          ClosePathLine(&Points,n,false,Tx);

        ReadFloat(&StrMovements, PositionX);
        if(*StrMovements==',') StrMovements++;
        while(isspace(*StrMovements)) StrMovements++;
        if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-')
        {
          ReadFloat(&StrMovements, PositionY);
        }
        //PositionOffsetX = PositionOffsetY = 0;
        FirstCommand = false;
        break;

      case 'm':			//relative move to.
        if(n>0) SegmentCount++;
        if(isCurve)
          ClosePathCurve(&Points,n,false,Tx);
        else
          ClosePathLine(&Points,n,false,Tx);

        ReadFloat(&StrMovements, val);
        if(FirstCommand)
            PositionX = val;
        else
        {
          PositionX += val;
        }

        if(*StrMovements==',') StrMovements++;
        while(isspace(*StrMovements)) StrMovements++;
        if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-')
        {
          ReadFloat(&StrMovements, val);
          if(FirstCommand)
            PositionY = val;
          else
          {
            PositionY += val;
          }
        }
        //PositionOffsetX = PositionOffsetY = 0;
        FirstCommand = false;
        break;

     case 'H':		//horizontal line to.
        LastCommand = 'H';
        if(isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathCurve(&Points,n,false,Tx);
          isCurve = false;
        }

        if(Points==NULL)
        {
          Points = (float*)malloc(4*sizeof(float));
          if(!Points) goto ExitErr;
          Points[0]=PositionX; Points[1]=PositionY;
          n = 2;
        }
        else
        {
          n++;
          Points = (float*)realloc(Points,2*n*sizeof(float));
          if(Points==NULL) goto ExitErr;
        }

        if(ReadFloat(&StrMovements, PositionX))
        {
          Points[2*n-2] = PositionX;
          Points[2*n-1] = PositionY;
        }
        break;

     case 'h':		// horizontal relative line to.
        LastCommand = 'h';
        if(isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathCurve(&Points,n,false,Tx);
          isCurve = false;
        }

        if(Points==NULL)
        {
          Points = (float*)malloc(4*sizeof(float));
          if(!Points) goto ExitErr;
          Points[0]=PositionX; Points[1]=PositionY;
          n = 2;
        }
        else
        {
          n++;
          Points = (float*)realloc(Points,2*n*sizeof(float));
          if(Points==NULL) goto ExitErr;
        }

        ReadFloat(&StrMovements, val);
        PositionX += val;
        Points[2*n-2] = PositionX;
        Points[2*n-1] = PositionY;
	break;

     case 'V':		//vertical line to.
        LastCommand = 'V';
        if(isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathCurve(&Points,n,false,Tx);
          isCurve = false;
        }

          if(Points==NULL)
          {
            Points = (float*)malloc(4*sizeof(float));
            if(!Points) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 2;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,2*n*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          if(ReadFloat(&StrMovements, PositionY))
          {
            Points[2*n-1] = PositionY;
            Points[2*n-2] = PositionX;
          }
        break;

     case 'v':		// vertical relative line to.
          LastCommand = 'v';
          if(isCurve)
          {
            if(n>0) SegmentCount++;
            ClosePathCurve(&Points,n,false,Tx);
            isCurve = false;
          }

          if(Points==NULL)
          {
            Points = (float*)malloc(4*sizeof(float));
            if(!Points) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 2;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,2*n*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          ReadFloat(&StrMovements, val);
          PositionY += val;
          Points[2*n-2] = PositionX;
          Points[2*n-1] = PositionY;
        break;

     case 'L':		//line to.
          LastCommand = 'L';
          if(isCurve)
          {
            if(n>0) SegmentCount++;
            ClosePathCurve(&Points,n,false,Tx);
            isCurve = false;
          }

          if(Points==NULL)
          {
            Points = (float*)malloc(4*sizeof(float));
            if(!Points) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 2;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,2*n*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          ReadFloat(&StrMovements, Points[2*n-2]);
          if(*StrMovements==',') StrMovements++;
          while(isspace(*StrMovements)) StrMovements++;
          if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-' || !strnicmp(StrMovements,"nan",3))
          {
            ReadFloat(&StrMovements, Points[2*n-1]);
          }
          else
            Points[2*n-1] = PositionY /* - PositionOffsetY */;

	  if(IsFinite(&Points[2*n-2]))
          {
            //Points[2*n-2] += PositionOffsetX;
            PositionX = Points[2*n-2];
          }
          else
            Points[2*n-2] = PositionX;
          if(IsFinite(&Points[2*n-1]))
          {
            //Points[2*n-1] += PositionOffsetY;          
            PositionY = Points[2*n-1];
          }
          else
            Points[2*n-1] = PositionY;
       break;

     case 'l':		//relative line to.
        LastCommand = 'l';
        if(isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathCurve(&Points,n,false,Tx);
          isCurve = false;
        }
          if(Points==NULL)
          {
            Points = (float*)malloc(4*sizeof(float));
            if(!Points) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 2;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,2*n*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          ReadFloat(&StrMovements, val);
          if(IsFinite(&val))
              PositionX += val;
          Points[2*n-2] = PositionX;
          if(*StrMovements==',') StrMovements++;
          while(isspace(*StrMovements)) StrMovements++;
          if(isdigit(*StrMovements) || *StrMovements=='.' || *StrMovements=='-' || !strnicmp(StrMovements,"nan",3))
          {
            ReadFloat(&StrMovements, val);
            if(IsFinite(&val))
                PositionY += val;
          }
          Points[2*n-1] = PositionY;
       break;

    case 'C':		//curve to.
        LastCommand = 'C';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

          if(Points==NULL)
          {
            Points=(float*)malloc(8*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 1;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          {
            float * const PointBase = Points + 2 + 6*n;
            ReadFloat(&StrMovements, PointBase[-6]); // x1
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-5]); // y1 - the control point at beginning of curve.
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-4]); // x2
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-3]); // y2 - the control point at end of curve.
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-2]); // x
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-1]); // y - the end point for curve.

            /* PointBase[-6] += PositionOffsetX;
            PointBase[-4] += PositionOffsetX;
            PointBase[-2] += PositionOffsetX;
            PointBase[-5] += PositionOffsetY;
            PointBase[-3] += PositionOffsetY;
            PointBase[-1] += PositionOffsetY; */
         }
        break;

    case 'Q':		//quadratic curve
        LastCommand = 'Q';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

          if(Points==NULL)
          {
            Points=(float*)malloc(8*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 1;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          {
            float * const PointBase = Points + 2 + 6*n;
            ReadFloat(&StrMovements, PointBase[-6]); // xt
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-5]); // yt
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-2]); // x
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, PointBase[-1]); // y
          
            PointBase[-4] = SolveQuadraticPos(PointBase[-2], PointBase[-6]);
            PointBase[-3] = SolveQuadraticPos(PointBase[-1], PointBase[-5]);
            PointBase[-6] = SolveQuadraticPos(PointBase[-8], PointBase[-6]);
            PointBase[-5] = SolveQuadraticPos(PointBase[-7], PointBase[-5]);

            /* PointBase[-6] += PositionOffsetX;
            PointBase[-4] += PositionOffsetX;
            PointBase[-2] += PositionOffsetX;
            PointBase[-5] += PositionOffsetY;
            PointBase[-3] += PositionOffsetY;
            PointBase[-1] += PositionOffsetY; */
          }
        break;

    case 'T':		//quadratic short curve
        LastCommand = 'T';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

        if(Points==NULL)
        {
          Points=(float*)malloc(8*sizeof(float));
          if(Points==NULL) goto ExitErr;
          Points[0]=PositionX; Points[1]=PositionY;
          n = 1;
        }
        else
        {
          n++;
          Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
          if(Points==NULL) goto ExitErr;
        }

        {
          float * const PointBase = Points + 2 + 6*n;
          ReadFloat(&StrMovements, PointBase[-2]); // x
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, PointBase[-1]); // y

          if(n<=1)
          {
            PointBase[-6] = PointBase[-4] = (PointBase[-8]+PointBase[-2])/2;
            PointBase[-5] = PointBase[-3] = (PointBase[-7]+PointBase[-1])/2;
          }
          else
          {
            val = (PointBase[-8]-PointBase[-10])*(1+sqrt(2.0)) + PointBase[-10];
            PointBase[-6] = SolveQuadraticPos(PointBase[-8], val);
            PointBase[-4] = SolveQuadraticPos(PointBase[-2], val);
            val = (PointBase[-7]-PointBase[-9])*(1+sqrt(2.0)) + PointBase[-9];
            PointBase[-5] = SolveQuadraticPos(PointBase[-7], val); 
            PointBase[-3] = SolveQuadraticPos(PointBase[-1], val);
          }

          /* PointBase[-6] += PositionOffsetX;
          PointBase[-4] += PositionOffsetX;
          PointBase[-2] += PositionOffsetX;
          PointBase[-5] += PositionOffsetY;
          PointBase[-3] += PositionOffsetY;
          PointBase[-1] += PositionOffsetY; */
        }
        break;

    case 'c':		//relative curve to.
        LastCommand = 'c';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

          if(Points==NULL)
          {
            Points=(float*)malloc(8*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 1;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

         {
            float * const PointBase = Points + 2 + 6*n;

            ReadFloat(&StrMovements, val);
            PointBase[-6] = PositionX + val;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PointBase[-5] = PositionY + val;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PointBase[-4] = PositionX + val;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PointBase[-3] = PositionY + val;

            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PositionX += val;
            PointBase[-2] = PositionX;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PositionY += val;
            PointBase[-1] = PositionY;
          }
        break;

    case 'q':		//relative quadratic curve to.
        LastCommand = 'q';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

          if(Points==NULL)
          {
            Points=(float*)malloc(8*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 1;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

          {
            float * const PointBase = Points + 2 + 6*n;

            ReadFloat(&StrMovements, val);
            PointBase[-6] = PositionX + val;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PointBase[-5] = PositionY + val;
            if(*StrMovements==',') StrMovements++;

            ReadFloat(&StrMovements, val);
            PositionX += val;
            PointBase[-2] = PositionX;
            if(*StrMovements==',') StrMovements++;
            ReadFloat(&StrMovements, val);
            PositionY += val;
            PointBase[-1] = PositionY;
            
            PointBase[-4] = SolveQuadraticPos(PointBase[-2], PointBase[-6]);
            PointBase[-3] = SolveQuadraticPos(PointBase[-1], PointBase[-5]);
            PointBase[-6] = SolveQuadraticPos(PointBase[-8], PointBase[-6]);
            PointBase[-5] = SolveQuadraticPos(PointBase[-7], PointBase[-5]);
          }
        break;

    case 'S':		// shorter curve to.
        LastCommand = 'S';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }
        if(Points==NULL)
        {
          Points=(float*)malloc(8*sizeof(float));
          if(Points==NULL) goto ExitErr;
          Points[0]=PositionX; Points[1]=PositionY;
          n = 1;
        }
        else
        {
          n++;
          Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
          if(Points==NULL) goto ExitErr;
        }

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

    case 's':		// relative shorter curve to.
        LastCommand = 's';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

          if(Points==NULL)
          {
            Points=(float*)malloc(8*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[0]=PositionX; Points[1]=PositionY;
            n = 1;
          }
          else
          {
            n++;
            Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
            if(Points==NULL) goto ExitErr;
          }

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

    case 't':		// relative shorter quadratic curve to
        LastCommand = 't';
        if(!isCurve)
        {
          if(n>0) SegmentCount++;
          ClosePathLine(&Points,n,false,Tx);
          isCurve = true;
        }

        if(Points==NULL)
        {
          Points=(float*)malloc(8*sizeof(float));
          if(Points==NULL) goto ExitErr;
          Points[0]=PositionX; Points[1]=PositionY;
          n = 1;
        }
        else
        {
          n++;
          Points = (float*)realloc(Points,(2+6*n)*sizeof(float));
          if(Points==NULL) goto ExitErr;
        }

        {
          float * const PointBase = Points + 2 + 6*n;
          ReadFloat(&StrMovements, val);
          PointBase[-2] = PositionX = PositionX + val;
          if(*StrMovements==',') StrMovements++;
          ReadFloat(&StrMovements, val);
          PointBase[-1] = PositionY = PositionY + val;

          if(n<=1)
          {
            PointBase[-6] = PointBase[-4] = (PointBase[-8]+PointBase[-2])/2;
            PointBase[-5] = PointBase[-3] = (PointBase[-7]+PointBase[-1])/2;
          }
          else
          {
            val = (PointBase[-8]-PointBase[-10])*(1+sqrt(2.0)) + PointBase[-10];
            PointBase[-6] = SolveQuadraticPos(PointBase[-8], val);
            PointBase[-4] = SolveQuadraticPos(PointBase[-2], val);
            val = (PointBase[-7]-PointBase[-9])*(1+sqrt(2.0)) + PointBase[-9];
            PointBase[-5] = SolveQuadraticPos(PointBase[-7], val); 
            PointBase[-3] = SolveQuadraticPos(PointBase[-1], val);
          }
        }
        break;

      case 'Z':		// close path
      case 'z':
        if(isCurve)
            ClosePathCurve(&Points, n, SegmentCount<=0, Tx);
        else
        {
/*        if(Points!=NULL && n>0)
          {
            n++;
            Points = (float*)realloc(Points,2*n*sizeof(float));
            if(Points==NULL) goto ExitErr;
            Points[2*n-2] = ObjPositionX+PositionOffsetX;
            Points[2*n-1] = ObjPositionY+PositionOffsetY;
          } */
          ClosePathLine(&Points, n, SegmentCount<=0, Tx);
        }
        SegmentCount = 0;
		// Z command breaks PATH.
        if(VectList.FirstObject != NULL)
        {
          VectorPath *pVectPath = new VectorPath;
          if(pVectPath)
          {
            pVectPath->AttribFromPSS(PSS);
            GetStyle(pVectPath,pVectPath);
            float stroke_width;
            if(GetProp("stroke-width",stroke_width))
            {
              pVectPath->PenWidth = stroke_width;
              if(pVectPath->LineStyle==0) pVectPath->LineStyle=1;
            }
            switch(GetColor(pVectPath->FillColor,FILL_COLOR))
            {
              case 0: pVectPath->BrushStyle=FILL_NONE;
                      break;
              case 1: if(pVectPath->BrushStyle==FILL_NONE) pVectPath->BrushStyle=FILL_SOLID;
	              //pVectPath->Filled = true;
                      break;
              case 2: SolveExtProperty(FILL_COLOR,pVectPath,pVectPath);
		      break;
              //default:
            }
            GetColor(pVectPath->LineColor);
            GetLineCap(pVectPath->LineCap);
            GetLineJoin(pVectPath->LineJoin);
            pVectPath->Close = true;	//Close;

            pVectPath->Swap(VectList);
            VectListIn.AddObject(pVectPath);
          }
        }
        break;

      default:
        if(isdigit(ch) || ch=='.' || ch=='-')
            StrMovements++;
        else
        {
          if(err!=NULL)
          {
            perc.Hide();
            fprintf(err,_("\nError: Unsupported path command '%c' in SVG path!"),ch);
          }
        }
        break;

   }

  }

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

  VectListIn.Swap(VectList);
  if(VectListIn.FirstObject!=NULL)
  {
    VectorPath *pVectPath = new VectorPath;
    if(pVectPath)
    {
      GetStyle(pVectPath,pVectPath);
      float stroke_width;
      if(GetProp("stroke-width",stroke_width))
      {
        pVectPath->PenWidth = stroke_width;
        if(pVectPath->LineStyle==0) pVectPath->LineStyle=1;
      }
      switch(GetColor(pVectPath->FillColor,FILL_COLOR))
      {
        case 0: pVectPath->BrushStyle=FILL_NONE;
                break;
        case 1: if(pVectPath->BrushStyle==FILL_NONE) pVectPath->BrushStyle=FILL_SOLID;
                //pVectPath->Filled = true;
                break;
        //default:
      }
      GetColor(pVectPath->LineColor);
      GetLineCap(pVectPath->LineCap);
      GetLineJoin(pVectPath->LineJoin);
      //pVectPath->Close = Close;

      pVectPath->Swap(VectListIn);
      VectList.AddObject(pVectPath);
    }
  }

  strcpy(ObjType,ObjName+1);
  if(Tx) delete Tx;

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

  return;

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


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);
    GetStyle(pVecLine,NULL);
    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)
  {

    VectorPolygon *pVecPoly = new VectorPolygon(Points, n); Points=NULL;
    pVecPoly->AttribFromPSS(PSS);
    GetStyle(pVecPoly,pVecPoly);
    if(GetProp("stroke-width",stroke_width))
    {
      pVecPoly->PenWidth = stroke_width;
    }
    GetColor(pVecPoly->LineColor);    
    switch(GetColor(pVecPoly->FillColor,FILL_COLOR))
    {
      case 0: pVecPoly->BrushStyle=FILL_NONE;
              break;
      case 1: if(pVecPoly->BrushStyle==FILL_NONE) pVecPoly->BrushStyle=FILL_SOLID;
              break;    
      //default:
    }
    GetLineCap(pVecPoly->LineCap);
    pVecPoly->Close = true;
    VectList.AddObject(pVecPoly);

    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, rx, ry;
float stroke_width;
VectorRectangle *pVectRec;
AbstractTransformXY *Tx = NULL;

  if(!GetProp("width",Width)) goto ExitErr;
  if(!GetProp("height",Height)) goto ExitErr;
  if(!GetProp("x",x)) x=0;
  if(!GetProp("y",y)) y=0;
  if(!GetProp("rx",rx)) rx=0;
  if(!GetProp("ry",ry)) ry=0;
  Tx = getTransform();

  if(rx>0 && ry>0)
    pVectRec = new VectorRectangleArc(y, y+Height, x, x+Width, rx, ry);
  else
    pVectRec = new VectorRectangle(y, y+Height, x, x+Width);
  pVectRec->AttribFromPSS(PSS);
  GetStyle(pVectRec,pVectRec);
  if(GetProp("stroke-width",stroke_width))
  {
    pVectRec->PenWidth = stroke_width;
  }
  GetColor(pVectRec->LineColor);
  switch(GetColor(pVectRec->FillColor,FILL_COLOR))
  {
    case 0: pVectRec->BrushStyle=FILL_NONE;
            break;
    case 1: if(pVectRec->BrushStyle==FILL_NONE) pVectRec->BrushStyle=FILL_SOLID;
            break;    
      //default:
  }

  if(Tx && pVectRec->Tx==NULL)
  {
    pVectRec->Tx = Tx;
    Tx = NULL;
  }
  pVectRec->CalcBoundingBox(bbx);

  VectList.AddObject(pVectRec);

  strcpy(ObjType,ObjName+1);
  if(Tx) delete Tx;
  return;

ExitErr:
  strcpy(ObjType,ObjName);
}


void TconvertedPass1_SVG::Defs(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Defs ");fflush(log);
#endif
int i;
FloatBBox bbxBk = bbx;	// Defines should not update current bounding box.

  if(subby==XML_closedtag) goto Finish;
  InitBBox(bbx);
  recursion++;
  while(!feof(wpd))
  {
    ReadXMLTag(false);

    if((by==XML_tag||by==XML_closedtag) && TAG=="<clipPath>")
    {
      DefinedObject *pDefObj = NULL;
      if((i=TAG_Args IN "id") >= 0)
      {
        const char * const Payload = TAG_Args.Member(i,1);
        if(Payload!=NULL)
        {
          pDefObj = new DefinedObject(Payload);
          if(pDefObj)
          {
            if(GetColor(pDefObj->FillColor,FILL_COLOR) == 1)
                pDefObj->PropsSet |= DefinedObject::EpropFillColor;
            if(GetColor(pDefObj->LineColor,LINE_COLOR) == 1)
                pDefObj->PropsSet |= DefinedObject::EpropLineColor;
          }
        }
      }
      if(by==XML_tag)
      {
        recursion++;
        while(!feof(wpd))
        {
          if(pDefObj) VectList.Swap(pDefObj->VectList);
          ProcessKeySVG();
          if(pDefObj) VectList.Swap(pDefObj->VectList);
          ReadXMLTag(false);

          if((by==XML_closetag||by==XML_closedclosetag) && (TAG=="</defs>" || TAG=="</clipPath>"))
              break;
        }
      }
      recursion--;
      if(pDefObj) AddDefine(pDefObj);
    }

    if((by==XML_tag||by==XML_closedtag) && (TAG=="<linearGradient>" || TAG=="<radialGradient>"))
    {
      DefinedObject *pDefObj = NULL;
      if((i=TAG_Args IN "id") >= 0)
      {
        const char * const Payload = TAG_Args.Member(i,1);
        if(Payload!=NULL)
        {
          pDefObj = new DefinedObject(Payload);
          if(pDefObj)
          {
            if(GetColor(pDefObj->FillColor,FILL_COLOR) == 1)
                pDefObj->PropsSet |= DefinedObject::EpropFillColor;
            if(GetColor(pDefObj->LineColor,LINE_COLOR) == 1)
                pDefObj->PropsSet |= DefinedObject::EpropLineColor;
          }
        }
      }

      if(by==XML_tag)
      {
        recursion++;
        while(!feof(wpd))
        {
          if(pDefObj) VectList.Swap(pDefObj->VectList);
          ProcessKeySVG();
          if(pDefObj) VectList.Swap(pDefObj->VectList);
          ReadXMLTag(false);

          if((by==XML_closetag||by==XML_closedclosetag) && (TAG=="</defs>" || TAG=="</linearGradient>" || TAG=="</radialGradient>"))
              break;
          if((by==XML_tag||by==XML_closedtag) && TAG=="<stop>")
          {          
            if(GetColor(pDefObj->FillColor, "stop-color") == 1)
            {          
              pDefObj->PropsSet |= DefinedObject::EpropFillColor;
              //if((i=TAG_Args IN "offset") >= 0)		// @TODO: Gradient is not supported yet.
              //{
              //}
            }
          }
        }
        recursion--;
      }
      if(pDefObj) AddDefine(pDefObj);
    }

    if((by==XML_closetag||by==XML_closedclosetag) && TAG=="</defs>")
        break;

    if((i=TAG_Args IN "id") >= 0)
    {
      const char * const Payload = TAG_Args.Member(i,1);
      if(Payload!=NULL)
      {
        DefinedObject *pDefObj = new DefinedObject(Payload);
        if(pDefObj!=NULL)
        {
          VectList.Swap(pDefObj->VectList);
          ProcessKeySVG();
          VectList.Swap(pDefObj->VectList);
          if(pDefObj->VectList.VectorObjects > 0)
              AddDefine(pDefObj);
          else
              delete pDefObj;
          continue;	// The command has been already processed.
        }
      }
    }

    if(TAG=="<style>")
    {
      Style();			// style could be nested inside defs.
    }

    const unsigned char BkFlag=flag;
    flag = Nothing;		// Prevent to expand anything inside Defs.
    ProcessKeySVG();
    flag = BkFlag;
  }
  recursion--;
  bbx = bbxBk;
Finish:
  strcpy(ObjType,"Defs");
}


/// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use
void TconvertedPass1_SVG::Use(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Use ");fflush(log);
#endif
static const char ObjName[] = "!Use";
float x, y;
int i;
TransformSVG *pTx = NULL;

  // href
  if(!GetProp("x",x)) x=0;
  if(!GetProp("y",y)) y=0;
  //Note: width, and height have no effect on use elements, unless the element referenced has a viewBox - i.e. they only have an effect when use refers to a svg or symbol element.
  // width
  // height

  if(x!=0 || y!=0)
  {
    pTx = new TransformSVG;
    pTx->CTM(3,0) = x;
    pTx->CTM(3,1) = y;
  }

  if((i=TAG_Args IN "xlink:href")>=0)
  {
    const char * const Payload = TAG_Args.Member(i,1);
    if(Payload==NULL || *Payload==0) goto ExitFail;
    if(Payload[0]!='#') goto ExitFail;

    const DefinedObject *pDObj = GetDefine(Payload+1);
    if(pDObj==NULL)		// The object was not found in SVG dictionary.
    {				// try to postpone processing.
      ForwardHolder *pFwdObj = new ForwardHolder(Payload+1, x, y);
      AddDelayedObject(pFwdObj);
      VectList.AddObject(pFwdObj);
      goto ExitFail;
    }
    VectorObject *pObj = pDObj->VectList.Duplicate();
    if(pObj==NULL) goto ExitFail;

    if(pTx) pObj->Transform(*pTx);	// Apply shift x,y operation.
    pObj->CalcBoundingBox(bbx);
    VectList.AddObject(pObj);

    strcpy(ObjType,ObjName+1);
    if(pTx) {delete(pTx);pTx=NULL;}
    return;
  }

ExitFail:
  strcpy(ObjType,ObjName);
  if(pTx) {delete(pTx);pTx=NULL;}
}


void TconvertedPass1_SVG::ProcessKeySVG(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG:::ProcessKeySVG() ");fflush(log);
#endif
string loc_TAG;
const char *tag;
uint8_t 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:
        case XML_closedtag:
               subby=by;
               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=SVG_RECT;break;}
	       if(loc_TAG=="circle")  {by=SVG_CIRCLE;break;}
	       if(loc_TAG=="ellipse") {by=SVG_ELIPSE;break;}
	       if(loc_TAG=="image")   {by=SVG_IMAGE;break;}
	       if(loc_TAG=="line")    {by=SVG_LINE;break;}
	       if(loc_TAG=="polyline"){by=SVG_POLYLINE;break;}
	       if(loc_TAG=="polygon") {by=SVG_POLYGON;break;}
	       if(loc_TAG=="path")    {by=SVG_PATH;break;}
	       if(loc_TAG=="g")       {by=SVG_G;break;}
	       if(loc_TAG=="a")       {by=SVG_A;break;}
	       if(loc_TAG=="text")    {by=SVG_TEXT;break;}
	       if(loc_TAG=="defs")    {by=SVG_DEFS;break;}
	       if(loc_TAG=="use")     {by=SVG_USE;break;}
	       if(loc_TAG=="style")   {by=SVG_STYLE;break;}
	       break;
	case XML_closetag:
        case XML_closedclosetag:
               subby=by;
               loc_TAG = copy(TAG,2,length(TAG)-3);	//Closing tag </xxx>
	       if((tag=loc_TAG())==NULL) break;
	       if(loc_TAG=="defs")    {by=SVG_DEFS;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 SVG_RECT:Rectangle(); break;
	case SVG_CIRCLE:Circle(); break;
	case SVG_ELIPSE:Ellipse(); break;
	case SVG_LINE:Line(); break;
	case SVG_POLYLINE:PolyLine(); break;
	case SVG_POLYGON:Polygon(); break;
	case SVG_PATH:Path(); break;
	case SVG_A:Anchor(); break;
	case SVG_G:Group(); break;
	case SVG_TEXT:Text(); break;
	case SVG_DEFS:Defs(); break;
	case SVG_USE:Use(); break;
	case SVG_IMAGE: ImageEmbed(); break;
	case SVG_STYLE: Style(); break;
	}


 this->by = by;				// restore local by & subby.
 this->subby = 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);
}


void TconvertedPass1_SVG::ProcessImage(void)
{
  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;
      }
    }
  }
}


void TconvertedPass1_SVG::SolveDelayedObjs(void)
{
  for(int i=0; i<DelayedObjCount; i++)
  {
    ForwardHolderBase *pObj = DelayedObjects[i];
    if(pObj==NULL) continue;

    const DefinedObject *pDefDObj = GetDefine(pObj->KeyHook());
    if(pDefDObj)
    {
      pObj->DoTheJob(pDefDObj,VectList);
      delete pObj;			// pObj is either replaced or not found, erase it.
    }
    // else pObj could be still referenced.
    DelayedObjects[i] = NULL;
  }
  EraseObjCache();
}


int TconvertedPass1_SVG::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_SVG::Convert_first_pass() ");fflush(log);
#endif
uint32_t 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;

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

  while(ActualPos < fsize)
  {
    ReadXMLTag(false);
    if(TAG=="<SVG>" || TAG=="<svg>")
    {
      if(GetColor(PSS.LineColor) > 0)
      {
        PSS.LineStyle = 1;
      }
      switch(GetColor(PSS.FillColor,FILL_COLOR))
      {
        case 0: PSS.FillPattern=FILL_NONE;
                break;
        case 1: if(PSS.FillPattern==FILL_NONE) PSS.FillPattern=FILL_SOLID;
	        //pVecCurve->Filled = true;
                break;
        //default:
      }
      break;
    }
  }  

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

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

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

    CheckOversizedImage(&Img);

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

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

    PutImageIncluder(NewFilename());

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

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


Image LoadPictureSVG(const char *Name)
{
TconvertedPass1_SVG ConvSvg;
size_t fsize;

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

      ConvSvg.TablePos = 0;
      ConvSvg.ActualPos = ConvSvg.DocumentStart = ftell(ConvSvg.wpd);
      fsize = FileSize(ConvSvg.wpd);
      //ConvSvg.perc.Init(ftell(ConvSvg.wpd), fsize,_("First pass SVG:") );

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

        ConvSvg.TAG.erase();
        ConvSvg.ProcessKeySVG();
      }

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

      ConvSvg.SolveDelayedObjs();
#ifdef _DEBUG
     //{FILE *F=fopen("O:\\temp\\24\\svg_file.txt","wb");ConvSvg.VectList.Dump2File(F);fclose(F);}
#endif
      ConvSvg.ProcessImage();
    }
  }

  return ConvSvg.Img;
}

