blob: d3abba6c4dfb8f91af03fba9fe42a46755afa5a1 [file] [log] [blame] [edit]
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// The SVG parser is based on Anti-Graim Geometry SVG example
// Copyright (C) 2002-2004 Maxim Shemanarev (McSeem)
#include "nanosvg.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif
#ifdef _MSC_VER
#pragma warning (disable: 4996) // Switch off security warnings
#endif
// Simple XML parser
#define TAG 1
#define CONTENT 2
#define MAX_ATTRIBS 256
static void parseContent(char* s,
void (*contentCb)(void* ud, const char* s),
void* ud)
{
// Trim start white spaces
while (*s && isspace(*s)) s++;
if (!*s) return;
if (contentCb)
(*contentCb)(ud, s);
}
static void parseElement(char* s,
void (*startelCb)(void* ud, const char* el, const char** attr),
void (*endelCb)(void* ud, const char* el),
void* ud)
{
const char* attr[MAX_ATTRIBS];
int nattr = 0;
char* name;
int start = 0;
int end = 0;
// Skip white space after the '<'
while (*s && isspace(*s)) s++;
// Check if the tag is end tag
if (*s == '/')
{
s++;
end = 1;
}
else
{
start = 1;
}
// Skip comments, data and preprocessor stuff.
if (!*s || *s == '?' || *s == '!')
return;
// Get tag name
name = s;
while (*s && !isspace(*s)) s++;
if (*s) { *s++ = '\0'; }
// Get attribs
while (!end && *s && nattr < MAX_ATTRIBS-1)
{
// Skip white space before the attrib name
while (*s && isspace(*s)) s++;
if (!*s) break;
if (*s == '/')
{
end = 1;
break;
}
attr[nattr++] = s;
// Find end of the attrib name.
while (*s && !isspace(*s) && *s != '=') s++;
if (*s) { *s++ = '\0'; }
// Skip until the beginning of the value.
while (*s && *s != '\"') s++;
if (!*s) break;
s++;
// Store value and find the end of it.
attr[nattr++] = s;
while (*s && *s != '\"') s++;
if (*s) { *s++ = '\0'; }
}
// List terminator
attr[nattr++] = 0;
attr[nattr++] = 0;
// Call callbacks.
if (start && startelCb)
(*startelCb)(ud, name, attr);
if (end && endelCb)
(*endelCb)(ud, name);
}
int parsexml(char* input,
void (*startelCb)(void* ud, const char* el, const char** attr),
void (*endelCb)(void* ud, const char* el),
void (*contentCb)(void* ud, const char* s),
void* ud)
{
char* s = input;
char* mark = s;
int state = CONTENT;
while (*s)
{
if (*s == '<' && state == CONTENT)
{
// Start of a tag
*s++ = '\0';
parseContent(mark, contentCb, ud);
mark = s;
state = TAG;
}
else if (*s == '>' && state == TAG)
{
// Start of a content or new tag.
*s++ = '\0';
parseElement(mark, startelCb, endelCb, ud);
mark = s;
state = CONTENT;
}
else
s++;
}
return 1;
}
/* Simple SVG parser. */
#define SVG_MAX_ATTR 128
struct SVGAttrib
{
float xform[6];
unsigned int fillColor;
unsigned int strokeColor;
float fillOpacity;
float strokeOpacity;
float strokeWidth;
char hasFill;
char hasStroke;
char visible;
};
struct SVGParser
{
struct SVGAttrib attr[SVG_MAX_ATTR];
int attrHead;
float* buf;
int nbuf;
int cbuf;
struct SVGPath* plist;
char pathFlag;
char defsFlag;
float tol;
};
static void xformSetIdentity(float* t)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = 0.0f; t[5] = 0.0f;
}
static void xformSetTranslation(float* t, float tx, float ty)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = tx; t[5] = ty;
}
static void xformSetScale(float* t, float sx, float sy)
{
t[0] = sx; t[1] = 0.0f;
t[2] = 0.0f; t[3] = sy;
t[4] = 0.0f; t[5] = 0.0f;
}
static void xformMultiply(float* t, float* s)
{
float t0 = t[0] * s[0] + t[1] * s[2];
float t2 = t[2] * s[0] + t[3] * s[2];
float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
t[1] = t[0] * s[1] + t[1] * s[3];
t[3] = t[2] * s[1] + t[3] * s[3];
t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
t[0] = t0;
t[2] = t2;
t[4] = t4;
}
static void xformPremultiply(float* t, float* s)
{
float s2[6];
memcpy(s2, s, sizeof(float)*6);
xformMultiply(s2, t);
memcpy(t, s2, sizeof(float)*6);
}
static struct SVGParser* svgCreateParser()
{
struct SVGParser* p;
p = (struct SVGParser*)malloc(sizeof(struct SVGParser));
if (!p)
return NULL;
memset(p, 0, sizeof(struct SVGParser));
// Init style
xformSetIdentity(p->attr[0].xform);
p->attr[0].fillColor = 0;
p->attr[0].strokeColor = 0;
p->attr[0].fillOpacity = 1;
p->attr[0].strokeOpacity = 1;
p->attr[0].strokeWidth = 1;
p->attr[0].hasFill = 0;
p->attr[0].hasStroke = 0;
p->attr[0].visible = 1;
return p;
}
static void svgDeleteParser(struct SVGParser* p)
{
struct SVGPath* path;
struct SVGPath* next;
path = p->plist;
while (path)
{
next = path->next;
if (path->pts)
free(path->pts);
free(path);
path = next;
}
if (p->buf)
free(p->buf);
free(p);
}
static void svgResetPath(struct SVGParser* p)
{
p->nbuf = 0;
}
static void svgPathPoint(struct SVGParser* p, float x, float y)
{
int cap;
float* buf;
if (p->nbuf+1 > p->cbuf)
{
cap = p->cbuf ? p->cbuf*2 : 8;
buf = (float*)malloc(cap*2*sizeof(float));
if (!buf) return;
if (p->nbuf)
memcpy(buf, p->buf, p->nbuf*2*sizeof(float));
if (p->buf)
free(p->buf);
p->buf = buf;
p->cbuf = cap;
}
p->buf[p->nbuf*2+0] = x;
p->buf[p->nbuf*2+1] = y;
p->nbuf++;
}
static struct SVGAttrib* svgGetAttr(struct SVGParser* p)
{
return &p->attr[p->attrHead];
}
static void svgPushAttr(struct SVGParser* p)
{
if (p->attrHead < SVG_MAX_ATTR-1)
{
p->attrHead++;
memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(struct SVGAttrib));
}
}
static void svgPopAttr(struct SVGParser* p)
{
if (p->attrHead > 0)
p->attrHead--;
}
static void svgCreatePath(struct SVGParser* p, char closed)
{
float* t;
float* pt;
struct SVGAttrib* attr;
struct SVGPath* path;
int i;
if (!p)
return;
if (!p->nbuf)
{
return;
}
attr = svgGetAttr(p);
path = (struct SVGPath*)malloc(sizeof(struct SVGPath));
if (!path)
return;
memset(path, 0, sizeof(struct SVGPath));
path->pts = (float*)malloc(p->nbuf*2*sizeof(float));
if (!path->pts)
{
free(path);
return;
}
path->closed = closed;
path->npts = p->nbuf;
path->next = p->plist;
p->plist = path;
// Transform path.
t = attr->xform;
for (i = 0; i < p->nbuf; ++i)
{
pt = &p->buf[i*2];
path->pts[i*2+0] = pt[0]*t[0] + pt[1]*t[2] + t[4];
path->pts[i*2+1] = pt[0]*t[1] + pt[1]*t[3] + t[5];
}
path->hasFill = attr->hasFill;
path->hasStroke = attr->hasStroke;
path->strokeWidth = attr->strokeWidth * t[0];
path->fillColor = attr->fillColor;
if (path->hasFill)
path->fillColor |= (unsigned int)(attr->fillOpacity*255) << 24;
path->strokeColor = attr->strokeColor;
if (path->hasStroke)
path->strokeColor |= (unsigned int)(attr->strokeOpacity*255) << 24;
}
static int isnum(char c)
{
return strchr("0123456789+-.eE", c) != 0;
}
/*static const char* parsePathFloats(const char* s, float* arg, int n)
{
char num[64];
const char* start;
int nnum;
int i = 0;
while (*s && i < n)
{
// Skip white spaces and commas
while (*s && (isspace(*s) || *s == ',')) s++;
if (!*s) break;
start = s;
nnum = 0;
while (*s && isnum(*s))
{
if (nnum < 63) num[nnum++] = *s;
s++;
}
num[nnum] = '\0';
arg[i++] = (float)atof(num);
}
return s;
}*/
static const char* getNextPathItem(const char* s, char* it)
{
int i = 0;
it[0] = '\0';
// Skip white spaces and commas
while (*s && (isspace(*s) || *s == ',')) s++;
if (!*s) return s;
if (*s == '-' || *s == '+' || isnum(*s))
{
while (*s == '-' || *s == '+')
{
if (i < 63) it[i++] = *s;
s++;
}
while (*s && *s != '-' && *s != '+' && isnum(*s))
{
if (i < 63) it[i++] = *s;
s++;
}
it[i] = '\0';
}
else
{
it[0] = *s++;
it[1] = '\0';
return s;
}
return s;
}
static unsigned int parseColor(const char* str)
{
unsigned c = 0;
while(*str == ' ') ++str;
if (*str == '#')
sscanf(str + 1, "%x", &c);
return c;
}
static float parseFloat(const char* str)
{
while (*str == ' ') ++str;
return (float)atof(str);
}
static int parseTransformArgs(const char* str, float* args, int maxNa, int* na)
{
const char* end;
const char* ptr;
*na = 0;
ptr = str;
while (*ptr && *ptr != '(') ++ptr;
if (*ptr == 0)
return 1;
end = ptr;
while (*end && *end != ')') ++end;
if (*end == 0)
return 1;
while (ptr < end)
{
if (isnum(*ptr))
{
if (*na >= maxNa) return 0;
args[(*na)++] = (float)atof(ptr);
while (ptr < end && isnum(*ptr)) ++ptr;
}
else
{
++ptr;
}
}
return (int)(end - str);
}
static int svgParseMatrix(struct SVGParser* p, const char* str)
{
float t[6];
int na = 0;
int len = parseTransformArgs(str, t, 6, &na);
if (na != 6) return len;
xformPremultiply(svgGetAttr(p)->xform, t);
return len;
}
static int svgParseTranslate(struct SVGParser* p, const char* str)
{
float args[2];
float t[6];
int na = 0;
int len = parseTransformArgs(str, args, 2, &na);
if (na == 1) args[1] = 0.0;
xformSetTranslation(t, args[0], args[1]);
xformPremultiply(svgGetAttr(p)->xform, t);
return len;
}
static int svgParseScale(struct SVGParser* p, const char* str)
{
float args[2];
int na = 0;
float t[6];
int len = parseTransformArgs(str, args, 2, &na);
if (na == 1) args[1] = args[0];
xformSetScale(t, args[0], args[1]);
xformPremultiply(svgGetAttr(p)->xform, t);
return len;
}
static void svgParseTransform(struct SVGParser* p, const char* str)
{
while (*str)
{
if (strncmp(str, "matrix", 6) == 0)
str += svgParseMatrix(p, str);
else if (strncmp(str, "translate", 9) == 0)
str += svgParseTranslate(p, str);
else if (strncmp(str, "scale", 5) == 0)
str += svgParseScale(p, str);
else
++str;
}
}
static void svgParseStyle(struct SVGParser* p, const char* str);
static int svgParseAttr(struct SVGParser* p, const char* name, const char* value)
{
struct SVGAttrib* attr = svgGetAttr(p);
if (!attr) return 0;
if (strcmp(name, "style") == 0)
{
svgParseStyle(p, value);
}
else if (strcmp(name, "display") == 0)
{
if (strcmp(value, "none") == 0)
attr->visible = 0;
else
attr->visible = 1;
}
else if (strcmp(name, "fill") == 0)
{
if (strcmp(value, "none") == 0)
{
attr->hasFill = 0;
}
else
{
attr->hasFill = 1;
attr->fillColor = parseColor(value);
}
}
else if (strcmp(name, "fill-opacity") == 0)
{
attr->fillOpacity = parseFloat(value);
}
else if (strcmp(name, "stroke") == 0)
{
if (strcmp(value, "none") == 0)
{
attr->hasStroke = 0;
}
else
{
attr->hasStroke = 1;
attr->strokeColor = parseColor(value);
}
}
else if (strcmp(name, "stroke-width") == 0)
{
attr->strokeWidth = parseFloat(value);
}
else if (strcmp(name, "stroke-opacity") == 0)
{
attr->strokeOpacity = parseFloat(value);
}
else if (strcmp(name, "transform") == 0)
{
svgParseTransform(p, value);
}
else
{
return 0;
}
return 1;
}
static int svgParseNameValue(struct SVGParser* p, const char* start, const char* end)
{
const char* str;
const char* val;
char name[512];
char value[512];
int n;
str = start;
while (str < end && *str != ':') ++str;
val = str;
// Right Trim
while (str > start && (*str == ':' || isspace(*str))) --str;
++str;
n = (int)(str - start);
if (n > 511) n = 511;
if (n) memcpy(name, start, n);
name[n] = 0;
while (val < end && (*val == ':' || isspace(*val))) ++val;
n = (int)(end - val);
if (n > 511) n = 511;
if (n) memcpy(value, val, n);
value[n] = 0;
return svgParseAttr(p, name, value);
}
static void svgParseStyle(struct SVGParser* p, const char* str)
{
const char* start;
const char* end;
while (*str)
{
// Left Trim
while(*str && isspace(*str)) ++str;
start = str;
while(*str && *str != ';') ++str;
end = str;
// Right Trim
while (end > start && (*end == ';' || isspace(*end))) --end;
++end;
svgParseNameValue(p, start, end);
if (*str) ++str;
}
}
static void svgParseAttribs(struct SVGParser* p, const char** attr)
{
int i;
for (i = 0; attr[i]; i += 2)
{
if (strcmp(attr[i], "style") == 0)
svgParseStyle(p, attr[i + 1]);
else
svgParseAttr(p, attr[i], attr[i + 1]);
}
}
static int getArgsPerElement(char cmd)
{
switch (tolower(cmd))
{
case 'v':
case 'h':
return 1;
case 'm':
case 'l':
case 't':
return 2;
case 'q':
case 's':
return 4;
case 'c':
return 6;
case 'a':
return 7;
}
return 0;
}
static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
{
float pqx, pqy, dx, dy, d, t;
pqx = qx-px;
pqy = qy-py;
dx = x-px;
dy = y-py;
d = pqx*pqx + pqy*pqy;
t = pqx*dx + pqy*dy;
if (d > 0) t /= d;
if (t < 0) t = 0;
else if (t > 1) t = 1;
dx = px + t*pqx - x;
dy = py + t*pqy - y;
return dx*dx + dy*dy;
}
static void cubicBezRec(struct SVGParser* p,
float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4,
int level)
{
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
float d;
if (level > 12) return;
x12 = (x1+x2)*0.5f;
y12 = (y1+y2)*0.5f;
x23 = (x2+x3)*0.5f;
y23 = (y2+y3)*0.5f;
x34 = (x3+x4)*0.5f;
y34 = (y3+y4)*0.5f;
x123 = (x12+x23)*0.5f;
y123 = (y12+y23)*0.5f;
x234 = (x23+x34)*0.5f;
y234 = (y23+y34)*0.5f;
x1234 = (x123+x234)*0.5f;
y1234 = (y123+y234)*0.5f;
d = distPtSeg(x1234, y1234, x1,y1, x4,y4);
if (level > 0 && d < p->tol*p->tol)
{
svgPathPoint(p, x1234, y1234);
return;
}
cubicBezRec(p, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1);
cubicBezRec(p, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1);
}
static void cubicBez(struct SVGParser* p,
float x1, float y1, float cx1, float cy1,
float cx2, float cy2, float x2, float y2)
{
cubicBezRec(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2, 0);
svgPathPoint(p, x2, y2);
}
static void quadBezRec(struct SVGParser* p,
float x1, float y1, float x2, float y2, float x3, float y3,
int level)
{
float x12,y12,x23,y23,x123,y123,d;
if (level > 12) return;
x12 = (x1+x2)*0.5f;
y12 = (y1+y2)*0.5f;
x23 = (x2+x3)*0.5f;
y23 = (y2+y3)*0.5f;
x123 = (x12+x23)*0.5f;
y123 = (y12+y23)*0.5f;
d = distPtSeg(x123, y123, x1,y1, x3,y3);
if (level > 0 && d < p->tol*p->tol)
{
svgPathPoint(p, x123, y123);
return;
}
quadBezRec(p, x1,y1, x12,y12, x123,y123, level+1);
quadBezRec(p, x123,y123, x23,y23, x3,y3, level+1);
}
static void quadBez(struct SVGParser* p,
float x1, float y1, float cx, float cy, float x2, float y2)
{
quadBezRec(p, x1,y1, cx,cy, x2,y2, 0);
svgPathPoint(p, x2, y2);
}
static void pathLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
if (rel)
{
*cpx += args[0];
*cpy += args[1];
}
else
{
*cpx = args[0];
*cpy = args[1];
}
svgPathPoint(p, *cpx, *cpy);
}
static void pathHLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
if (rel)
*cpx += args[0];
else
*cpx = args[0];
svgPathPoint(p, *cpx, *cpy);
}
static void pathVLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
if (rel)
*cpy += args[0];
else
*cpy = args[0];
svgPathPoint(p, *cpx, *cpy);
}
static void pathCubicBezTo(struct SVGParser* p, float* cpx, float* cpy,
float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
x1 = *cpx;
y1 = *cpy;
if (rel)
{
cx1 = *cpx + args[0];
cy1 = *cpy + args[1];
cx2 = *cpx + args[2];
cy2 = *cpy + args[3];
x2 = *cpx + args[4];
y2 = *cpy + args[5];
}
else
{
cx1 = args[0];
cy1 = args[1];
cx2 = args[2];
cy2 = args[3];
x2 = args[4];
y2 = args[5];
}
cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx2;
*cpy2 = cy2;
*cpx = x2;
*cpy = y2;
}
static void pathCubicBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
x1 = *cpx;
y1 = *cpy;
if (rel)
{
cx2 = *cpx + args[0];
cy2 = *cpy + args[1];
x2 = *cpx + args[2];
y2 = *cpy + args[3];
}
else
{
cx2 = args[0];
cy2 = args[1];
x2 = args[2];
y2 = args[3];
}
cx1 = 2*x1 - *cpx2;
cy1 = 2*y1 - *cpy2;
cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx2;
*cpy2 = cy2;
*cpx = x2;
*cpy = y2;
}
static void pathQuadBezTo(struct SVGParser* p, float* cpx, float* cpy,
float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx, cy;
x1 = *cpx;
y1 = *cpy;
if (rel)
{
cx = *cpx + args[0];
cy = *cpy + args[1];
x2 = *cpx + args[2];
y2 = *cpy + args[3];
}
else
{
cx = args[0];
cy = args[1];
x2 = args[2];
y2 = args[3];
}
quadBez(p, x1,y1, cx,cy, x2,y2);
*cpx2 = cx;
*cpy2 = cy;
*cpx = x2;
*cpy = y2;
}
static void pathQuadBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx, cy;
x1 = *cpx;
y1 = *cpy;
if (rel)
{
x2 = *cpx + args[0];
y2 = *cpy + args[1];
}
else
{
x2 = args[0];
y2 = args[1];
}
cx = 2*x1 - *cpx2;
cy = 2*y1 - *cpy2;
quadBez(p, x1,y1, cx,cy, x2,y2);
*cpx2 = cx;
*cpy2 = cy;
*cpx = x2;
*cpy = y2;
}
static void svgParsePath(struct SVGParser* p, const char** attr)
{
const char* s;
char cmd;
float args[10];
int nargs;
int rargs;
float cpx, cpy, cpx2, cpy2;
const char* tmp[4];
char closedFlag;
int i;
char item[64];
for (i = 0; attr[i]; i += 2)
{
if (strcmp(attr[i], "d") == 0)
{
s = attr[i + 1];
svgResetPath(p);
closedFlag = 0;
nargs = 0;
while (*s)
{
s = getNextPathItem(s, item);
if (!*item) break;
if (isnum(item[0]))
{
if (nargs < 10)
args[nargs++] = (float)atof(item);
if (nargs >= rargs)
{
switch (cmd)
{
case 'm':
case 'M':
case 'l':
case 'L':
pathLineTo(p, &cpx, &cpy, args, (cmd == 'm' || cmd == 'l') ? 1 : 0);
break;
case 'H':
case 'h':
pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
break;
case 'V':
case 'v':
pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
break;
case 'C':
case 'c':
pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
break;
case 'S':
case 's':
pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
break;
case 'Q':
case 'q':
pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
break;
case 'T':
case 't':
pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
break;
default:
if (nargs >= 2)
{
cpx = args[nargs-2];
cpy = args[nargs-1];
}
break;
}
nargs = 0;
}
}
else
{
cmd = item[0];
rargs = getArgsPerElement(cmd);
if (cmd == 'M' || cmd == 'm')
{
// Commit path.
if (p->nbuf)
svgCreatePath(p, closedFlag);
// Start new subpath.
svgResetPath(p);
closedFlag = 0;
nargs = 0;
cpx = 0; cpy = 0;
}
else if (cmd == 'Z' || cmd == 'z')
{
closedFlag = 1;
// Commit path.
if (p->nbuf)
svgCreatePath(p, closedFlag);
// Start new subpath.
svgResetPath(p);
closedFlag = 0;
nargs = 0;
}
}
}
// Commit path.
if (p->nbuf)
svgCreatePath(p, closedFlag);
}
else
{
tmp[0] = attr[i];
tmp[1] = attr[i + 1];
tmp[2] = 0;
tmp[3] = 0;
svgParseAttribs(p, tmp);
}
}
}
static void svgParseRect(struct SVGParser* p, const char** attr)
{
float x = 0.0f;
float y = 0.0f;
float w = 0.0f;
float h = 0.0f;
int i;
for (i = 0; attr[i]; i += 2)
{
if (!svgParseAttr(p, attr[i], attr[i + 1]))
{
if (strcmp(attr[i], "x") == 0) x = parseFloat(attr[i+1]);
if (strcmp(attr[i], "y") == 0) y = parseFloat(attr[i+1]);
if (strcmp(attr[i], "width") == 0) w = parseFloat(attr[i+1]);
if (strcmp(attr[i], "height") == 0) h = parseFloat(attr[i+1]);
}
}
if (w != 0.0f && h != 0.0f)
{
svgResetPath(p);
svgPathPoint(p, x, y);
svgPathPoint(p, x+w, y);
svgPathPoint(p, x+w, y+h);
svgPathPoint(p, x, y+h);
svgCreatePath(p, 1);
}
}
static void svgParseCircle(struct SVGParser* p, const char** attr)
{
float cx = 0.0f;
float cy = 0.0f;
float r = 0.0f;
float da;
int i,n;
float x,y,u;
for (i = 0; attr[i]; i += 2)
{
if (!svgParseAttr(p, attr[i], attr[i + 1]))
{
if (strcmp(attr[i], "cx") == 0) cx = parseFloat(attr[i+1]);
if (strcmp(attr[i], "cy") == 0) cy = parseFloat(attr[i+1]);
if (strcmp(attr[i], "r") == 0) r = fabsf(parseFloat(attr[i+1]));
}
}
if (r != 0.0f)
{
svgResetPath(p);
da = acosf(r/(r+p->tol))*2;
n = (int)ceilf(M_PI*2/da);
da = (float)(M_PI*2)/n;
for (i = 0; i < n; ++i)
{
u = i*da;
x = cx + cosf(u)*r;
y = cy + sinf(u)*r;
svgPathPoint(p, x, y);
}
svgCreatePath(p, 1);
}
}
static void svgParseLine(struct SVGParser* p, const char** attr)
{
float x1 = 0.0;
float y1 = 0.0;
float x2 = 0.0;
float y2 = 0.0;
int i;
for (i = 0; attr[i]; i += 2)
{
if (!svgParseAttr(p, attr[i], attr[i + 1]))
{
if (strcmp(attr[i], "x1") == 0) x1 = parseFloat(attr[i + 1]);
if (strcmp(attr[i], "y1") == 0) y1 = parseFloat(attr[i + 1]);
if (strcmp(attr[i], "x2") == 0) x2 = parseFloat(attr[i + 1]);
if (strcmp(attr[i], "y2") == 0) y2 = parseFloat(attr[i + 1]);
}
}
svgResetPath(p);
svgPathPoint(p, x1, y1);
svgPathPoint(p, x2, y2);
svgCreatePath(p, 0);
}
static void svgParsePoly(struct SVGParser* p, const char** attr, int closeFlag)
{
int i;
const char* s;
float args[2];
int nargs;
char item[64];
svgResetPath(p);
for (i = 0; attr[i]; i += 2)
{
if (!svgParseAttr(p, attr[i], attr[i + 1]))
{
if (strcmp(attr[i], "points") == 0)
{
s = attr[i + 1];
nargs = 0;
while (*s)
{
s = getNextPathItem(s, item);
args[nargs++] = (float)atof(item);
if (nargs >= 2)
{
svgPathPoint(p, args[0], args[1]);
nargs = 0;
}
}
}
}
}
svgCreatePath(p, closeFlag);
}
static void svgStartElement(void* ud, const char* el, const char** attr)
{
struct SVGParser* p = (struct SVGParser*)ud;
// Skip everything in defs
if (p->defsFlag)
return;
if (strcmp(el, "g") == 0)
{
svgPushAttr(p);
svgParseAttribs(p, attr);
}
else if (strcmp(el, "path") == 0)
{
if (p->pathFlag) // Do not allow nested paths.
return;
svgPushAttr(p);
svgParsePath(p, attr);
p->pathFlag = 1;
svgPopAttr(p);
}
else if (strcmp(el, "rect") == 0)
{
svgPushAttr(p);
svgParseRect(p, attr);
svgPopAttr(p);
}
else if (strcmp(el, "circle") == 0)
{
svgPushAttr(p);
svgParseCircle(p, attr);
svgPopAttr(p);
}
else if (strcmp(el, "line") == 0)
{
svgPushAttr(p);
svgParseLine(p, attr);
svgPopAttr(p);
}
else if (strcmp(el, "polyline") == 0)
{
svgPushAttr(p);
svgParsePoly(p, attr, 0);
svgPopAttr(p);
}
else if (strcmp(el, "polygon") == 0)
{
svgPushAttr(p);
svgParsePoly(p, attr, 1);
svgPopAttr(p);
}
else if (strcmp(el, "defs") == 0)
{
p->defsFlag = 1;
}
}
static void svgEndElement(void* ud, const char* el)
{
struct SVGParser* p = (struct SVGParser*)ud;
if (strcmp(el, "g") == 0)
{
svgPopAttr(p);
}
else if (strcmp(el, "path") == 0)
{
p->pathFlag = 0;
}
else if (strcmp(el, "defs") == 0)
{
p->defsFlag = 0;
}
}
static void svgContent(void* ud, const char* s)
{
// empty
}
struct SVGPath* svgParse(char* input)
{
struct SVGParser* p;
struct SVGPath* ret = 0;
p = svgCreateParser();
if (!p)
return 0;
p->tol = 1.0f;
parsexml(input, svgStartElement, svgEndElement, svgContent, p);
if (p->buf)
{
free(p->buf);
p->buf = NULL;
p->nbuf = 0;
p->cbuf = 0;
}
ret = p->plist;
p->plist = 0;
svgDeleteParser(p);
return ret;
}
struct SVGPath* svgParseFromFile(const char* filename)
{
FILE* fp;
int size;
char* data;
struct SVGPath* plist;
fp = fopen(filename, "rb");
if (!fp) return 0;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
data = (char*)malloc(size+1);
fread(data, size, 1, fp);
data[size] = '\0'; // Must be null terminated.
fclose(fp);
plist = svgParse(data);
free(data);
return plist;
}
void svgDelete(struct SVGPath* plist)
{
struct SVGPath* path;
struct SVGPath* next;
if (!plist)
return;
path = plist;
while (path)
{
next = path->next;
if (path->pts)
free(path->pts);
free(path);
path = next;
}
}