aa700821a8
Update to current version of NanoSVG Pull Request: https://projects.blender.org/blender/blender/pulls/109948
648 lines
19 KiB
Diff
648 lines
19 KiB
Diff
diff --git a/extern/nanosvg/nanosvg.h b/extern/nanosvg/nanosvg.h
|
|
index 60a323820cb..1bfb891c397 100644
|
|
--- a/extern/nanosvg/nanosvg.h
|
|
+++ b/extern/nanosvg/nanosvg.h
|
|
@@ -1,56 +1,58 @@
|
|
/*
|
|
* Copyright (c) 2013-14 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-Grain Geometry 2.4 SVG example
|
|
* Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
|
|
*
|
|
* Arc calculation code based on canvg (https://code.google.com/p/canvg/)
|
|
*
|
|
* Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
|
*
|
|
+ * This is a modified version for Blender used by importers.
|
|
+ *
|
|
*/
|
|
|
|
#ifndef NANOSVG_H
|
|
#define NANOSVG_H
|
|
|
|
#ifndef NANOSVG_CPLUSPLUS
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#endif
|
|
|
|
// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
|
|
//
|
|
// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
|
|
//
|
|
// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
|
|
//
|
|
// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
|
|
// That is, you should get the same looking data as your designed in your favorite app.
|
|
//
|
|
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
|
|
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
|
|
//
|
|
// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
|
|
// DPI (dots-per-inch) controls how the unit conversion is done.
|
|
//
|
|
// If you don't know or care about the units stuff, "px" and 96 should get you going.
|
|
|
|
|
|
/* Example Usage:
|
|
@@ -112,60 +114,61 @@ typedef struct NSVGgradientStop {
|
|
} NSVGgradientStop;
|
|
|
|
typedef struct NSVGgradient {
|
|
float xform[6];
|
|
char spread;
|
|
float fx, fy;
|
|
int nstops;
|
|
NSVGgradientStop stops[1];
|
|
} NSVGgradient;
|
|
|
|
typedef struct NSVGpaint {
|
|
signed char type;
|
|
union {
|
|
unsigned int color;
|
|
NSVGgradient* gradient;
|
|
};
|
|
} NSVGpaint;
|
|
|
|
typedef struct NSVGpath
|
|
{
|
|
float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
|
|
int npts; // Total number of bezier points.
|
|
char closed; // Flag indicating if shapes should be treated as closed.
|
|
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
|
struct NSVGpath* next; // Pointer to next path, or NULL if last element.
|
|
} NSVGpath;
|
|
|
|
typedef struct NSVGshape
|
|
{
|
|
char id[64]; // Optional 'id' attr of the shape or its group
|
|
+ char id_parent[64]; // Blender: Parent ID used for layer creation.
|
|
NSVGpaint fill; // Fill paint
|
|
NSVGpaint stroke; // Stroke paint
|
|
float opacity; // Opacity of the shape.
|
|
float strokeWidth; // Stroke width (scaled).
|
|
float strokeDashOffset; // Stroke dash offset (scaled).
|
|
float strokeDashArray[8]; // Stroke dash array (scaled).
|
|
char strokeDashCount; // Number of dash values in dash array.
|
|
char strokeLineJoin; // Stroke join type.
|
|
char strokeLineCap; // Stroke cap type.
|
|
float miterLimit; // Miter limit
|
|
char fillRule; // Fill rule, see NSVGfillRule.
|
|
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
|
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
|
char fillGradient[64]; // Optional 'id' of fill gradient
|
|
char strokeGradient[64]; // Optional 'id' of stroke gradient
|
|
float xform[6]; // Root transformation for fill/stroke gradient
|
|
NSVGpath* paths; // Linked list of paths in the image.
|
|
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
|
} NSVGshape;
|
|
|
|
typedef struct NSVGimage
|
|
{
|
|
float width; // Width of the image.
|
|
float height; // Height of the image.
|
|
NSVGshape* shapes; // Linked list of shapes in the image.
|
|
} NSVGimage;
|
|
|
|
// Parses SVG file from a file, returns SVG image as paths.
|
|
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
|
|
|
|
@@ -333,60 +336,61 @@ int nsvg__parseXML(char* input,
|
|
void* ud)
|
|
{
|
|
char* s = input;
|
|
char* mark = s;
|
|
int state = NSVG_XML_CONTENT;
|
|
while (*s) {
|
|
if (*s == '<' && state == NSVG_XML_CONTENT) {
|
|
// Start of a tag
|
|
*s++ = '\0';
|
|
nsvg__parseContent(mark, contentCb, ud);
|
|
mark = s;
|
|
state = NSVG_XML_TAG;
|
|
} else if (*s == '>' && state == NSVG_XML_TAG) {
|
|
// Start of a content or new tag.
|
|
*s++ = '\0';
|
|
nsvg__parseElement(mark, startelCb, endelCb, ud);
|
|
mark = s;
|
|
state = NSVG_XML_CONTENT;
|
|
} else {
|
|
s++;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Simple SVG parser. */
|
|
|
|
#define NSVG_MAX_ATTR 128
|
|
+#define NSVG_MAX_BREADCRUMB 5
|
|
|
|
enum NSVGgradientUnits {
|
|
NSVG_USER_SPACE = 0,
|
|
NSVG_OBJECT_SPACE = 1
|
|
};
|
|
|
|
#define NSVG_MAX_DASHES 8
|
|
|
|
enum NSVGunits {
|
|
NSVG_UNITS_USER,
|
|
NSVG_UNITS_PX,
|
|
NSVG_UNITS_PT,
|
|
NSVG_UNITS_PC,
|
|
NSVG_UNITS_MM,
|
|
NSVG_UNITS_CM,
|
|
NSVG_UNITS_IN,
|
|
NSVG_UNITS_PERCENT,
|
|
NSVG_UNITS_EM,
|
|
NSVG_UNITS_EX
|
|
};
|
|
|
|
typedef struct NSVGcoordinate {
|
|
float value;
|
|
int units;
|
|
} NSVGcoordinate;
|
|
|
|
typedef struct NSVGlinearData {
|
|
NSVGcoordinate x1, y1, x2, y2;
|
|
} NSVGlinearData;
|
|
|
|
@@ -428,60 +432,64 @@ typedef struct NSVGattrib
|
|
int strokeDashCount;
|
|
char strokeLineJoin;
|
|
char strokeLineCap;
|
|
float miterLimit;
|
|
char fillRule;
|
|
float fontSize;
|
|
unsigned int stopColor;
|
|
float stopOpacity;
|
|
float stopOffset;
|
|
char hasFill;
|
|
char hasStroke;
|
|
char visible;
|
|
} NSVGattrib;
|
|
|
|
typedef struct NSVGparser
|
|
{
|
|
NSVGattrib attr[NSVG_MAX_ATTR];
|
|
int attrHead;
|
|
float* pts;
|
|
int npts;
|
|
int cpts;
|
|
NSVGpath* plist;
|
|
NSVGimage* image;
|
|
NSVGgradientData* gradients;
|
|
NSVGshape* shapesTail;
|
|
float viewMinx, viewMiny, viewWidth, viewHeight;
|
|
int alignX, alignY, alignType;
|
|
float dpi;
|
|
char pathFlag;
|
|
char defsFlag;
|
|
+ /** Blender breadcrumb for layers. */
|
|
+ char breadcrumb[NSVG_MAX_BREADCRUMB][64];
|
|
+ /** Blender number of elements in breadcrumb. */
|
|
+ int breadcrumb_len;
|
|
} NSVGparser;
|
|
|
|
static void nsvg__xformIdentity(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 nsvg__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 nsvg__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 nsvg__xformSetSkewX(float* t, float a)
|
|
{
|
|
t[0] = 1.0f; t[1] = 0.0f;
|
|
t[2] = tanf(a); t[3] = 1.0f;
|
|
t[4] = 0.0f; t[5] = 0.0f;
|
|
}
|
|
|
|
@@ -930,60 +938,68 @@ static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
|
|
bounds[2] = curveBounds[2];
|
|
bounds[3] = curveBounds[3];
|
|
first = 0;
|
|
} else {
|
|
bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
|
|
bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
|
|
bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
|
|
bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
|
|
}
|
|
curve[0] = curve[6];
|
|
curve[1] = curve[7];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void nsvg__addShape(NSVGparser* p)
|
|
{
|
|
NSVGattrib* attr = nsvg__getAttr(p);
|
|
float scale = 1.0f;
|
|
NSVGshape* shape;
|
|
NSVGpath* path;
|
|
int i;
|
|
|
|
if (p->plist == NULL)
|
|
return;
|
|
|
|
shape = (NSVGshape*)malloc(sizeof(NSVGshape));
|
|
if (shape == NULL) goto error;
|
|
memset(shape, 0, sizeof(NSVGshape));
|
|
|
|
+ /* Copy parent id from breadcrumb. */
|
|
+ if (p->breadcrumb_len > 0) {
|
|
+ memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent);
|
|
+ }
|
|
+ else {
|
|
+ memcpy(shape->id_parent, attr->id, sizeof shape->id_parent);
|
|
+ }
|
|
+
|
|
memcpy(shape->id, attr->id, sizeof shape->id);
|
|
memcpy(shape->fillGradient, attr->fillGradient, sizeof shape->fillGradient);
|
|
memcpy(shape->strokeGradient, attr->strokeGradient, sizeof shape->strokeGradient);
|
|
memcpy(shape->xform, attr->xform, sizeof shape->xform);
|
|
scale = nsvg__getAverageScale(attr->xform);
|
|
shape->strokeWidth = attr->strokeWidth * scale;
|
|
shape->strokeDashOffset = attr->strokeDashOffset * scale;
|
|
shape->strokeDashCount = (char)attr->strokeDashCount;
|
|
for (i = 0; i < attr->strokeDashCount; i++)
|
|
shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
|
|
shape->strokeLineJoin = attr->strokeLineJoin;
|
|
shape->strokeLineCap = attr->strokeLineCap;
|
|
shape->miterLimit = attr->miterLimit;
|
|
shape->fillRule = attr->fillRule;
|
|
shape->opacity = attr->opacity;
|
|
|
|
shape->paths = p->plist;
|
|
p->plist = NULL;
|
|
|
|
// Calculate shape bounds
|
|
shape->bounds[0] = shape->paths->bounds[0];
|
|
shape->bounds[1] = shape->paths->bounds[1];
|
|
shape->bounds[2] = shape->paths->bounds[2];
|
|
shape->bounds[3] = shape->paths->bounds[3];
|
|
for (path = shape->paths->next; path != NULL; path = path->next) {
|
|
shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
|
|
shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
|
|
shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
|
|
shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
|
|
}
|
|
@@ -1175,66 +1191,75 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
|
|
if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
|
|
if (i < last) it[i++] = *s;
|
|
s++;
|
|
if (*s == '-' || *s == '+') {
|
|
if (i < last) it[i++] = *s;
|
|
s++;
|
|
}
|
|
while (*s && nsvg__isdigit(*s)) {
|
|
if (i < last) it[i++] = *s;
|
|
s++;
|
|
}
|
|
}
|
|
it[i] = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
static const char* nsvg__getNextPathItemWhenArcFlag(const char* s, char* it)
|
|
{
|
|
it[0] = '\0';
|
|
while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
|
|
if (!*s) return s;
|
|
if (*s == '0' || *s == '1') {
|
|
it[0] = *s++;
|
|
it[1] = '\0';
|
|
return s;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
-static const char* nsvg__getNextPathItem(const char* s, char* it)
|
|
+static const char* nsvg__getNextPathItem(const char* s, char* it, char cmd, int nargs)
|
|
{
|
|
it[0] = '\0';
|
|
// Skip white spaces and commas
|
|
while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
|
|
if (!*s) return s;
|
|
+
|
|
+ /* Blender: Special case for arc command's 4th and 5th arguments. */
|
|
+ if ((cmd == 'a' || cmd == 'A') && (nargs == 3 || nargs == 4)) {
|
|
+ it[0] = s[0];
|
|
+ it[1] = '\0';
|
|
+ s++;
|
|
+ return s;
|
|
+ }
|
|
+
|
|
if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
|
|
s = nsvg__parseNumber(s, it, 64);
|
|
} else {
|
|
// Parse command
|
|
it[0] = *s++;
|
|
it[1] = '\0';
|
|
return s;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static unsigned int nsvg__parseColorHex(const char* str)
|
|
{
|
|
unsigned int r=0, g=0, b=0;
|
|
if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex
|
|
return NSVG_RGB(r, g, b);
|
|
if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa
|
|
return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), ..
|
|
return NSVG_RGB(128, 128, 128);
|
|
}
|
|
|
|
// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters).
|
|
// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors
|
|
// for backwards compatibility. Note: other image viewers return black instead.
|
|
|
|
static unsigned int nsvg__parseColorRGB(const char* str)
|
|
{
|
|
int i;
|
|
unsigned int rgbi[3];
|
|
@@ -2264,61 +2289,61 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|
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];
|
|
} else {
|
|
tmp[0] = attr[i];
|
|
tmp[1] = attr[i + 1];
|
|
tmp[2] = 0;
|
|
tmp[3] = 0;
|
|
nsvg__parseAttribs(p, tmp);
|
|
}
|
|
}
|
|
|
|
if (s) {
|
|
nsvg__resetPath(p);
|
|
cpx = 0; cpy = 0;
|
|
cpx2 = 0; cpy2 = 0;
|
|
initPoint = 0;
|
|
closedFlag = 0;
|
|
nargs = 0;
|
|
|
|
while (*s) {
|
|
item[0] = '\0';
|
|
if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4))
|
|
s = nsvg__getNextPathItemWhenArcFlag(s, item);
|
|
if (!*item)
|
|
- s = nsvg__getNextPathItem(s, item);
|
|
+ s = nsvg__getNextPathItem(s, item, cmd, nargs);
|
|
if (!*item) break;
|
|
if (cmd != '\0' && nsvg__isCoordinate(item)) {
|
|
if (nargs < 10)
|
|
args[nargs++] = (float)nsvg__atof(item);
|
|
if (nargs >= rargs) {
|
|
switch (cmd) {
|
|
case 'm':
|
|
case 'M':
|
|
nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
|
|
// Moveto can be followed by multiple coordinate pairs,
|
|
// which should be treated as linetos.
|
|
cmd = (cmd == 'm') ? 'l' : 'L';
|
|
rargs = nsvg__getArgsPerElement(cmd);
|
|
cpx2 = cpx; cpy2 = cpy;
|
|
initPoint = 1;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
|
|
cpx2 = cpx; cpy2 = cpy;
|
|
break;
|
|
case 'H':
|
|
case 'h':
|
|
nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
|
|
cpx2 = cpx; cpy2 = cpy;
|
|
break;
|
|
case 'V':
|
|
case 'v':
|
|
nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
|
|
cpx2 = cpx; cpy2 = cpy;
|
|
@@ -2534,61 +2559,61 @@ static void nsvg__parseLine(NSVGparser* p, const char** attr)
|
|
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
|
|
}
|
|
}
|
|
|
|
nsvg__resetPath(p);
|
|
|
|
nsvg__moveTo(p, x1, y1);
|
|
nsvg__lineTo(p, x2, y2);
|
|
|
|
nsvg__addPath(p, 0);
|
|
|
|
nsvg__addShape(p);
|
|
}
|
|
|
|
static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
|
|
{
|
|
int i;
|
|
const char* s;
|
|
float args[2];
|
|
int nargs, npts = 0;
|
|
char item[64];
|
|
|
|
nsvg__resetPath(p);
|
|
|
|
for (i = 0; attr[i]; i += 2) {
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
|
|
if (strcmp(attr[i], "points") == 0) {
|
|
s = attr[i + 1];
|
|
nargs = 0;
|
|
while (*s) {
|
|
- s = nsvg__getNextPathItem(s, item);
|
|
+ s = nsvg__getNextPathItem(s, item, '\0', nargs);
|
|
args[nargs++] = (float)nsvg__atof(item);
|
|
if (nargs >= 2) {
|
|
if (npts == 0)
|
|
nsvg__moveTo(p, args[0], args[1]);
|
|
else
|
|
nsvg__lineTo(p, args[0], args[1]);
|
|
nargs = 0;
|
|
npts++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsvg__addPath(p, (char)closeFlag);
|
|
|
|
nsvg__addShape(p);
|
|
}
|
|
|
|
static void nsvg__parseSVG(NSVGparser* p, const char** attr)
|
|
{
|
|
int i;
|
|
for (i = 0; attr[i]; i += 2) {
|
|
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
|
|
if (strcmp(attr[i], "width") == 0) {
|
|
p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
|
|
} else if (strcmp(attr[i], "height") == 0) {
|
|
p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
|
|
} else if (strcmp(attr[i], "viewBox") == 0) {
|
|
const char *s = attr[i + 1];
|
|
@@ -2740,108 +2765,122 @@ static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
|
|
if (idx != grad->nstops-1) {
|
|
for (i = grad->nstops-1; i > idx; i--)
|
|
grad->stops[i] = grad->stops[i-1];
|
|
}
|
|
|
|
stop = &grad->stops[idx];
|
|
stop->color = curAttr->stopColor;
|
|
stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
|
|
stop->offset = curAttr->stopOffset;
|
|
}
|
|
|
|
static void nsvg__startElement(void* ud, const char* el, const char** attr)
|
|
{
|
|
NSVGparser* p = (NSVGparser*)ud;
|
|
|
|
if (p->defsFlag) {
|
|
// Skip everything but gradients in defs
|
|
if (strcmp(el, "linearGradient") == 0) {
|
|
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
|
|
} else if (strcmp(el, "radialGradient") == 0) {
|
|
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
|
|
} else if (strcmp(el, "stop") == 0) {
|
|
nsvg__parseGradientStop(p, attr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (strcmp(el, "g") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parseAttribs(p, attr);
|
|
+
|
|
+ /* Save the breadcrumb of groups. */
|
|
+ if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) {
|
|
+ NSVGattrib *attr_id = nsvg__getAttr(p);
|
|
+ memcpy(
|
|
+ p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len]));
|
|
+ p->breadcrumb_len++;
|
|
+ }
|
|
} else if (strcmp(el, "path") == 0) {
|
|
if (p->pathFlag) // Do not allow nested paths.
|
|
return;
|
|
nsvg__pushAttr(p);
|
|
nsvg__parsePath(p, attr);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "rect") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parseRect(p, attr);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "circle") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parseCircle(p, attr);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "ellipse") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parseEllipse(p, attr);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "line") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parseLine(p, attr);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "polyline") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parsePoly(p, attr, 0);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "polygon") == 0) {
|
|
nsvg__pushAttr(p);
|
|
nsvg__parsePoly(p, attr, 1);
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "linearGradient") == 0) {
|
|
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
|
|
} else if (strcmp(el, "radialGradient") == 0) {
|
|
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
|
|
} else if (strcmp(el, "stop") == 0) {
|
|
nsvg__parseGradientStop(p, attr);
|
|
} else if (strcmp(el, "defs") == 0) {
|
|
p->defsFlag = 1;
|
|
} else if (strcmp(el, "svg") == 0) {
|
|
nsvg__parseSVG(p, attr);
|
|
}
|
|
}
|
|
|
|
static void nsvg__endElement(void* ud, const char* el)
|
|
{
|
|
NSVGparser* p = (NSVGparser*)ud;
|
|
|
|
if (strcmp(el, "g") == 0) {
|
|
+ /* Remove the breadcrumb level. */
|
|
+ if (p->breadcrumb_len > 0) {
|
|
+ p->breadcrumb[p->breadcrumb_len - 1][0] = '\0';
|
|
+ p->breadcrumb_len--;
|
|
+ }
|
|
+
|
|
nsvg__popAttr(p);
|
|
} else if (strcmp(el, "path") == 0) {
|
|
p->pathFlag = 0;
|
|
} else if (strcmp(el, "defs") == 0) {
|
|
p->defsFlag = 0;
|
|
}
|
|
}
|
|
|
|
static void nsvg__content(void* ud, const char* s)
|
|
{
|
|
NSVG_NOTUSED(ud);
|
|
NSVG_NOTUSED(s);
|
|
// empty
|
|
}
|
|
|
|
static void nsvg__imageBounds(NSVGparser* p, float* bounds)
|
|
{
|
|
NSVGshape* shape;
|
|
shape = p->image->shapes;
|
|
if (shape == NULL) {
|
|
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
|
|
return;
|
|
}
|
|
bounds[0] = shape->bounds[0];
|
|
bounds[1] = shape->bounds[1];
|
|
bounds[2] = shape->bounds[2];
|
|
bounds[3] = shape->bounds[3];
|
|
for (shape = shape->next; shape != NULL; shape = shape->next) {
|
|
bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
|
|
bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
|