#include "svgparser.h"
#define MID(lo, hi) (lo + ((hi - lo) >> 1))
#define CHAR_EOF -1
#define CHAR_BAD -2
#define SvgIsSpaceChar(x) \
((x)==0x20 || (x)==0x0A || (x)==0x0D || (x)==0x09)
typedef struct{
char *str;
LONG pos;
LONG length;
} STRINGFILE;
typedef struct{
int type;
union {
double real;
LONG integer;
} v;
} SVGNUMBER;
LONG PEEKCHAR(STRINGFILE* sf) {
LONG c;
DWORD oldpos=sf->pos;
if(sf->pos>=sf->length)
return CHAR_EOF;
c=sf->str[sf->pos];
sf->pos=oldpos;
return c;
}
LONG NEXTCHAR(STRINGFILE*sf){
LONG c;
if(sf->pos>=sf->length)
return CHAR_EOF;
c=sf->str[sf->pos];
sf->pos++;
return c;
}
int SvgParseSep(STRINGFILE*sbuf){
LONG c;
DWORD beginpos=sbuf->pos;
c=PEEKCHAR(sbuf);
if(c==CHAR_EOF)
return 0;
else if(SvgIsSpaceChar(c)){
do {
NEXTCHAR(sbuf);
c=PEEKCHAR(sbuf);
} while(SvgIsSpaceChar(c));
}
if(c==','){
do {
NEXTCHAR(sbuf);
c=PEEKCHAR(sbuf);
} while(SvgIsSpaceChar(c));
}
return (sbuf->pos==beginpos)?0:1;
}
int SvgParseSepOrParen(STRINGFILE*sbuf){
LONG c;
DWORD beginpos=sbuf->pos;
c=PEEKCHAR(sbuf);
if(c==CHAR_EOF)
return 0;
else if(SvgIsSpaceChar(c)){
do {
NEXTCHAR(sbuf);
c=PEEKCHAR(sbuf);
} while(SvgIsSpaceChar(c));
}
if(c==')'){
NEXTCHAR(sbuf);
return 2;
}
if(c==','){
do {
NEXTCHAR(sbuf);
c=PEEKCHAR(sbuf);
} while(SvgIsSpaceChar(c));
}
return (sbuf->pos==beginpos)?0:1;
}
BOOL SvgParseSpace(STRINGFILE*sbuf){
BOOL ret=FALSE;
LONG c;
c=PEEKCHAR(sbuf);
while(SvgIsSpaceChar(c)){
ret=TRUE;
NEXTCHAR(sbuf);
c=PEEKCHAR(sbuf);
}
return ret;
}
BOOL SvgParseString(STRINGFILE*sbuf,char *string){
LONG c;
DWORD oldpos=sbuf->pos;
while(*string){
int c2=*string++;
c=NEXTCHAR(sbuf);
if(c!=c2){
sbuf->pos=oldpos;
return FALSE;
}
}
return TRUE;
}
BOOL SvgParseInteger(STRINGFILE*sbuf,LONG*pint){
int sign=1;
LONG dec=0;
int deccount=0;
LONG c=PEEKCHAR(sbuf);
if(c=='+')
sign=1;
else if(c=='-')
sign=-1;
else if(c>='0'&&c<='9'){
dec=dec*10+(c-'0');
deccount++;
}
else {
return FALSE;
}
NEXTCHAR(sbuf);
while(1){
c=PEEKCHAR(sbuf);
if(c>='0'&&c<='9'){
dec=dec*10+(c-'0');
deccount++;
}
else {
if(deccount==0)
return FALSE;
*pint=dec*sign;
return TRUE;
}
NEXTCHAR(sbuf);
}
return FALSE;
}
BOOL SvgParseNumberEx(STRINGFILE*sbuf,SVGNUMBER*num, BOOL lax){
LONG c;
int sign=1;
LONG dec=0;
double decReal=0.0;
double decFrac=0.0;
double decInc=10;
int deccount=0;
LONG exp=0;
int mode=0;
LONG exppos=0;
c=PEEKCHAR(sbuf);
if(c=='+')
sign=1;
else if(c=='-')
sign=-1;
else if(c>='0'&&c<='9'){
dec=dec*10+(c-'0');
decReal=decReal*10+(c-'0');
deccount++;
}
else if(c=='.'){
dec=0;
decReal=0;
mode=1;
}
else {
return FALSE;
}
NEXTCHAR(sbuf);
while(mode==0){
c=PEEKCHAR(sbuf);
if(c>='0'&&c<='9'){
dec=dec*10+(c-'0');
decReal=decReal*10+(c-'0');
deccount++;
}
else if(c=='E'||c=='e'){
if(deccount==0)
return FALSE;
exppos=sbuf->pos;
mode=2;
}
else if(c=='.'){
mode=1;
deccount=0;
}
else {
if(deccount==0)
return FALSE;
num->type=1;
num->v.integer=dec*sign;
return TRUE;
}
NEXTCHAR(sbuf);
}
while(mode==1){
c=PEEKCHAR(sbuf);
if(c>='0'&&c<='9'){
decFrac+=((double)(c-'0'))/decInc;
decInc*=10;
deccount++;
}
else if(c=='E'||c=='e'){
if(deccount==0&&!lax)
return FALSE;
exppos=sbuf->pos;
mode=2;
}
else {
if(deccount==0&&!lax)
return FALSE;
num->type=2;
num->v.real=(decReal+decFrac)*sign;
return TRUE;
}
NEXTCHAR(sbuf);
}
ASSERT(mode==2);
decReal=(decReal+decFrac)*sign;
deccount=0;
sign=1;
c=PEEKCHAR(sbuf);
if(c=='+')
sign=1;
else if(c=='-')
sign=-1;
else if(c>='0'&&c<='9'){
exp=c-'0';
deccount++;
}
else {
sbuf->pos=exppos;
num->type=2;
num->v.real=(decReal+decFrac)*sign;
return TRUE;
}
NEXTCHAR(sbuf);
while(1){
c=PEEKCHAR(sbuf);
if(c>='0'&&c<='9'){
exp=exp*10+(c-'0');
deccount++;
}
else {
if(deccount==0)
return FALSE;
exp*=sign;
num->type=2;
num->v.real=decReal*pow(10.0,exp);
return TRUE;
}
NEXTCHAR(sbuf);
}
return FALSE;
}
BOOL SvgParseDouble(STRINGFILE*sbuf,double *v){
SVGNUMBER num;
if(SvgParseNumberEx(sbuf,&num,FALSE)){
if(num.type==2)
*v=num.v.real;
else {
*v=(double)num.v.integer;
}
return TRUE;
}
return FALSE;
}
BOOL SvgParseDoubleSpace(STRINGFILE*sbuf,double *v){
BOOL ret=SvgParseDouble(sbuf,v);
SvgParseSpace(sbuf);
return ret;
}
BOOL SvgParseDoubleSep(STRINGFILE*sbuf,double *v){
BOOL ret=SvgParseDouble(sbuf,v);
SvgParseSep(sbuf);
return ret;
}
BOOL SvgParseDoubleLax(STRINGFILE*sbuf,double *v){
SVGNUMBER num;
if(SvgParseNumberEx(sbuf,&num,TRUE)){
if(num.type==2)
*v=num.v.real;
else {
*v=(double)num.v.integer;
}
return TRUE;
}
return FALSE;
}
BOOL SvgParseDoubleSepLax(STRINGFILE*sbuf,double *v){
BOOL ret=SvgParseDoubleLax(sbuf,v);
SvgParseSep(sbuf);
return ret;
}
BOOL SvgParseFlag(STRINGFILE*sbuf,BOOL*v){
LONG c=PEEKCHAR(sbuf);
*v=0;
if(c=='1')
*v=1;
else if(c!='0'){
return FALSE;
}
NEXTCHAR(sbuf);
return SvgParseSep(sbuf);
}
typedef struct{
int coord;
double curx;
double cury;
double movx;
double movy;
double cx,cy;
LONG iaIndex;
LONG lastcmd;
LONG prevcmd;
STRINGFILE sf;
ITEMARRAY ia;
} SVGPATHPARSER;
int SvgPathParserNext(LPVOID pspp, PATHSEGMENT *ps);
LPVOID SvgPathParserCreate(char *path);
void SvgPathParserFree(LPVOID pspp);
int SvgPathParserGetWinding(LPVOID p){
return WIND_NONZERO;
}
LPVOID SvgPathParserCreate(char *path){
SVGPATHPARSER *spp;
if(!path)
return NULL;
spp=malloc(sizeof(SVGPATHPARSER));
if(!spp)return NULL;
ItemArrayInitEx(&spp->ia,sizeof(PATHSEGMENT),5,10);
spp->coord=0;
spp->curx=0.0;
spp->cury=0.0;
spp->movx=0.0;
spp->movy=0.0;
spp->lastcmd=0;
spp->prevcmd=0;
spp->coord=0;
spp->sf.str=path;
spp->sf.pos=0;
spp->sf.length=strlen(path);
return spp;
}
void SvgPathParserFree(LPVOID pspp){
SVGPATHPARSER *spp=(SVGPATHPARSER *)pspp;
if(!spp)return;
ItemArrayFree(&spp->ia);
free(spp);
}
static int SvgNextMarker(SVGPATHPARSER *spp){
LONG c;
SvgParseSpace(&spp->sf);
c=PEEKCHAR(&spp->sf);
spp->prevcmd=spp->lastcmd;
switch(c){
case CHAR_EOF:
spp->coord=0;
spp->lastcmd=c;
return c;
case 'M': case 'm':
case 'L': case 'l':
case 'H': case 'h':
case 'V': case 'v':
case 'Q': case 'q':
case 'C': case 'c':
case 'S': case 's':
case 'T': case 't':
case 'A': case 'a':
spp->coord=0;
NEXTCHAR(&spp->sf);
SvgParseSpace(&spp->sf);
spp->lastcmd=c;
return c;
case 'Z': case 'z':
spp->coord=0;
NEXTCHAR(&spp->sf);
spp->lastcmd=c;
return c;
default:
spp->coord=1;
return spp->lastcmd;
}
}
int SvgPathParserNext(LPVOID pspp, PATHSEGMENT *ps){
LONG c;
SVGPATHPARSER *spp=(SVGPATHPARSER *)pspp;
if(!spp||!ps)return -1;
while(1){
if(spp->ia.length>0){
if(spp->iaIndex<spp->ia.length){
*ps=*(PATHSEGMENT*)ItemArrayPtr(&spp->ia,spp->iaIndex);
spp->iaIndex++;
if(spp->iaIndex>=spp->ia.length){
spp->ia.length=0;
}
return 1;
} else {
spp->ia.length=0;
}
}
switch(spp->lastcmd){
case CHAR_EOF:
return 0;
case 0:
SvgNextMarker(spp);
if(spp->lastcmd!=CHAR_EOF &&
spp->lastcmd!='m' &&
spp->lastcmd!='M'){
return -1;
}
break;
case 'M':
case 'm':
case 'L':
case 'l':{
double x,y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='m'||spp->lastcmd=='l'){
x+=spp->curx;
y+=spp->cury;
}
if(spp->coord==0 && (spp->lastcmd=='M'||spp->lastcmd=='m')){
ps->type=PATH_MOVETO;
spp->movx=x;
spp->movy=y;
} else {
ps->type=PATH_LINETO;
}
ps->coords[0]=x;
ps->coords[1]=y;
spp->curx=x;
spp->cury=y;
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'H':
case 'h':{
double x;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleLax(&spp->sf,&x)){
return -1;
}
if(spp->lastcmd=='h'){
x+=spp->curx;
}
ps->type=PATH_LINETO;
ps->coords[0]=x;
ps->coords[1]=spp->cury;
spp->curx=x;
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'V':
case 'v':{
double y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='v'){
y+=spp->cury;
}
ps->type=PATH_LINETO;
ps->coords[0]=spp->curx;
ps->coords[1]=y;
spp->cury=y;
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'Q':
case 'q':{
double x1,y1,x,y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&x1)||
!SvgParseDoubleSepLax(&spp->sf,&y1)||
!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='q'){
x1+=spp->curx;
y1+=spp->cury;
x+=spp->curx;
y+=spp->cury;
}
ps->type=PATH_QUADTO;
ps->coords[0]=x1;
ps->coords[1]=y1;
ps->coords[2]=x;
ps->coords[3]=y;
spp->curx=x;
spp->cury=y;
spp->cx=x1;
spp->cy=y1;
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'T':
case 't':{
double x,y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='t'){
x+=spp->curx;
y+=spp->cury;
}
if(spp->prevcmd=='Q'||
spp->prevcmd=='q'||
spp->prevcmd=='T'||
spp->prevcmd=='t'){
ps->coords[0]=spp->curx+spp->curx-spp->cx;
ps->coords[1]=spp->cury+spp->cury-spp->cy;
} else {
ps->coords[0]=spp->curx;
ps->coords[1]=spp->cury;
}
ps->type=PATH_QUADTO;
ps->coords[2]=x;
ps->coords[3]=y;
spp->curx=x;
spp->cury=y;
spp->cx=ps->coords[0];
spp->cy=ps->coords[1];
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'C':
case 'c':{
double x1,y1,x2,y2,x,y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&x1)||
!SvgParseDoubleSepLax(&spp->sf,&y1)||
!SvgParseDoubleSepLax(&spp->sf,&x2)||
!SvgParseDoubleSepLax(&spp->sf,&y2)||
!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='c'){
x1+=spp->curx;
y1+=spp->cury;
x2+=spp->curx;
y2+=spp->cury;
x+=spp->curx;
y+=spp->cury;
}
ps->type=PATH_CUBICTO;
ps->coords[0]=x1;
ps->coords[1]=y1;
ps->coords[2]=x2;
ps->coords[3]=y2;
ps->coords[4]=x;
ps->coords[5]=y;
spp->curx=x;
spp->cury=y;
spp->cx=x2;
spp->cy=y2;
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'S':
case 's':{
double x2,y2,x,y;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&x2)||
!SvgParseDoubleSepLax(&spp->sf,&y2)||
!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(spp->lastcmd=='s'){
x2+=spp->curx;
y2+=spp->cury;
x+=spp->curx;
y+=spp->cury;
}
if(spp->prevcmd=='C'||
spp->prevcmd=='c'||
spp->prevcmd=='S'||
spp->prevcmd=='s'){
ps->coords[0]=spp->curx+spp->curx-spp->cx;
ps->coords[1]=spp->cury+spp->cury-spp->cy;
} else {
ps->coords[0]=spp->curx;
ps->coords[1]=spp->cury;
}
ps->type=PATH_CUBICTO;
ps->coords[2]=x2;
ps->coords[3]=y2;
ps->coords[4]=x;
ps->coords[5]=y;
spp->curx=x;
spp->cury=y;
spp->cx=ps->coords[2];
spp->cy=ps->coords[3];
spp->lastcmd=SvgNextMarker(spp);
return 1;
}
case 'A':
case 'a':{
double rx,ry,rot,x,y;
BOOL arcflag,sweepflag;
if(spp->coord>0)
SvgParseSep(&spp->sf);
if(!SvgParseDoubleSepLax(&spp->sf,&rx)||
!SvgParseDoubleSepLax(&spp->sf,&ry)||
!SvgParseDoubleLax(&spp->sf,&rot)||
!SvgParseSep(&spp->sf)||
!SvgParseFlag(&spp->sf,&arcflag)||
!SvgParseFlag(&spp->sf,&sweepflag)||
!SvgParseDoubleSepLax(&spp->sf,&x)||
!SvgParseDoubleLax(&spp->sf,&y)){
return -1;
}
if(rx<0 || ry<0){
return -1;
}
if(spp->lastcmd=='a'){
x+=spp->curx;
y+=spp->cury;
}
spp->iaIndex=1;
spp->ia.length=0;
PathAddSvgArc(&spp->ia,spp->curx,spp->cury,x,y,
rx,ry,rot*DEGTORAD,arcflag,sweepflag);
spp->curx=x;
spp->cury=y;
spp->lastcmd=SvgNextMarker(spp);
break;
}
case 'Z':
case 'z':
if(spp->coord>0)
return -1;
ps->type=PATH_CLOSE;
spp->curx=spp->movx;
spp->cury=spp->movy;
spp->lastcmd=SvgNextMarker(spp);
return 1;
default:
return -1;
}
}
return -1;
}
PATHITERATOR SvgPathParser={
SvgPathParserCreate,
SvgPathParserNext,
SvgPathParserFree,
SvgPathParserGetWinding
};