Newer
Older
/*
* Copyright (c) 2015 Tricoire Sebastien 3dsman@free.fr
*
* 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.
*
*/
//OE_svgParser::OE_svg_base();
opacity = 0; // Opacity of the curve.
strokeWidth = 0; // Stroke width (scaled).
strokeLineJoin = 0; // Stroke join type.
strokeLineCap = 0; // Stroke cap type.
float OE_svgParser::vmag(float x, float y)
{
return sqrtf(x*x + y*y);
}
float OE_svgParser::vecrat(float ux, float uy, float vx, float vy)
float OE_svgParser::vecang(float ux, float uy, float vx, float vy)
{
float r = vecrat(ux,uy, vx,vy);
if (r < -1.0f) r = -1.0f;
if (r > 1.0f) r = 1.0f;
return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
}
void OE_svgParser::xformPoint(float* dx, float* dy, float x, float y, float* t)
{
*dx = x*t[0] + y*t[2] + t[4];
*dy = x*t[1] + y*t[3] + t[5];
}
void OE_svgParser::xformVec(float* dx, float* dy, float x, float y, float* t)
const char* OE_svgParser::parseNumber(const char* s, char* it, const int size)
if (*s == '-' || *s == '+')
{
if (i < last)
{
it[i++] = *s;
}
while (*s && isdigit(*s))
{
if (i < last)
{
it[i++] = *s;
}
while (*s && isdigit(*s))
{
if (i < last)
{
it[i++] = *s;
}
if (*s == 'e' || *s == 'E')
{
if (i < last)
{
it[i++] = *s;
}
if (*s == '-' || *s == '+')
{
if (i < last)
{
it[i++] = *s;
}
while (*s && isdigit(*s))
{
if (i < last)
{
it[i++] = *s;
}
void OE_svgParser::pathMoveTo(OE_pointcurve* curve, float* cpx, float* cpy, float* args, int rel)
void OE_svgParser::pathLineTo(OE_pointcurve* curve, float* cpx, float* cpy, float* args, int rel)
*cpx = args[0];
*cpy = args[1];
}
curve->lineTo(*cpx, *cpy);
}
void OE_svgParser::pathHLineTo(OE_pointcurve* curve, float* cpx, float* cpy, float* args, int rel)
void OE_svgParser::pathVLineTo(OE_pointcurve* curve, float* cpx, float* cpy, float* args, int rel)
void OE_svgParser::pathCubicBezTo(OE_pointcurve* curve, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int 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];
cx1 = args[0];
cy1 = args[1];
cx2 = args[2];
cy2 = args[3];
x2 = args[4];
y2 = args[5];
}
curve->cubicBezTo(cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx2;
*cpy2 = cy2;
*cpx = x2;
*cpy = y2;
}
void OE_svgParser::pathCubicBezShortTo(OE_pointcurve* curve, 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;
cx2 = *cpx + args[0];
cy2 = *cpy + args[1];
x2 = *cpx + args[2];
y2 = *cpy + args[3];
cx2 = args[0];
cy2 = args[1];
x2 = args[2];
y2 = args[3];
}
cx1 = 2*x1 - *cpx2;
cy1 = 2*y1 - *cpy2;
curve->cubicBezTo(cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx2;
*cpy2 = cy2;
*cpx = x2;
*cpy = y2;
}
void OE_svgParser::pathQuadBezTo(OE_pointcurve* curve, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx, cy;
float cx1, cy1, cx2, cy2;
x1 = *cpx;
y1 = *cpy;
cx = *cpx + args[0];
cy = *cpy + args[1];
x2 = *cpx + args[2];
y2 = *cpy + args[3];
cx = args[0];
cy = args[1];
x2 = args[2];
y2 = args[3];
}
// Convert to cubic bezier
cx1 = x1 + 2.0f/3.0f*(cx - x1);
cy1 = y1 + 2.0f/3.0f*(cy - y1);
cx2 = x2 + 2.0f/3.0f*(cx - x2);
cy2 = y2 + 2.0f/3.0f*(cy - y2);
curve->cubicBezTo(cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx;
*cpy2 = cy;
*cpx = x2;
*cpy = y2;
}
void OE_svgParser::pathQuadBezShortTo(OE_pointcurve* curve, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel)
{
float x1, y1, x2, y2, cx, cy;
float cx1, cy1, cx2, cy2;
x1 = *cpx;
y1 = *cpy;
x2 = args[0];
y2 = args[1];
}
cx = 2*x1 - *cpx2;
cy = 2*y1 - *cpy2;
// Convert to cubix bezier
cx1 = x1 + 2.0f/3.0f*(cx - x1);
cy1 = y1 + 2.0f/3.0f*(cy - y1);
cx2 = x2 + 2.0f/3.0f*(cx - x2);
cy2 = y2 + 2.0f/3.0f*(cy - y2);
curve->cubicBezTo(cx1,cy1, cx2,cy2, x2,y2);
*cpx2 = cx;
*cpy2 = cy;
*cpx = x2;
*cpy = y2;
}
void OE_svgParser::pathArcTo(OE_pointcurve* curve, float* cpx, float* cpy, float* args, int rel)
{
// Ported from canvg (https://code.google.com/p/canvg/)
float rx, ry, rotx;
float x1, y1, x2, y2, cx, cy, dx, dy, d;
float x1p, y1p, cxp, cyp, s, sa, sb;
float ux, uy, vx, vy, a1, da;
float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
float sinrx, cosrx;
int fa, fs;
int i, ndivs;
float hda, kappa;
rx = fabsf(args[0]); // y radius
ry = fabsf(args[1]); // x radius
rotx = args[2] / 180.0f * M_PI; // x rotation engle
fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
x1 = *cpx; // start point
x2 = args[5];
y2 = args[6];
}
dx = x1 - x2;
dy = y1 - y2;
d = sqrtf(dx*dx + dy*dy);
// The arc degenerates to a line
curve->lineTo(x2, y2);
*cpx = x2;
*cpy = y2;
return;
}
sinrx = sinf(rotx);
cosrx = cosf(rotx);
// Convert to center point parameterization.
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
// 1) Compute x1', y1'
x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
d = sqr(x1p)/sqr(rx) + sqr(y1p)/sqr(ry);
d = sqrtf(d);
rx *= d;
ry *= d;
}
// 2) Compute cx', cy'
s = 0.0f;
sa = sqr(rx)*sqr(ry) - sqr(rx)*sqr(y1p) - sqr(ry)*sqr(x1p);
sb = sqr(rx)*sqr(y1p) + sqr(ry)*sqr(x1p);
cxp = s * rx * y1p / ry;
cyp = s * -ry * x1p / rx;
// 3) Compute cx,cy from cx',cy'
cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
// 4) Calculate theta1, and delta theta.
ux = (x1p - cxp) / rx;
uy = (y1p - cyp) / ry;
vx = (-x1p - cxp) / rx;
vy = (-y1p - cyp) / ry;
a1 = vecang(1.0f,0.0f, ux,uy); // Initial angle
da = vecang(ux,uy, vx,vy); // Delta angle
// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
}
// Approximate the arc using cubic spline segments.
t[0] = cosrx; t[1] = sinrx;
t[2] = -sinrx; t[3] = cosrx;
t[4] = cx; t[5] = cy;
// Split arc into max 90 degree segments.
// The loop assumes an iteration per end point (including start and end), this +1.
ndivs = (int)(fabsf(da) / (M_PI*0.5f) + 1.0f);
hda = (da / (float)ndivs) / 2.0f;
kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
if (da < 0.0f)
a = a1 + da * (i/(float)ndivs);
dx = cosf(a);
dy = sinf(a);
xformPoint(&x, &y, dx*rx, dy*ry, t); // position
xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
if (i > 0)
px = x;
py = y;
ptanx = tanx;
ptany = tany;
}
*cpx = x2;
*cpy = y2;
}
const char* OE_svgParser::getNextPathItem(const char* s, char* it)
while (*s && (isspace(*s) || *s == ','))
{
s++;
}
if (!*s)
{
return s;
}
if (*s == '-' || *s == '+' || *s == '.' || isdigit(*s))
{
// Parse command
it[0] = *s++;
it[1] = '\0';
return s;
}
return s;
}
int OE_svgParser::getArgsPerElement(char cmd)
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
case 'v':
case 'V':
case 'h':
case 'H':
return 1;
case 'm':
case 'M':
case 'l':
case 'L':
case 't':
case 'T':
return 2;
case 'q':
case 'Q':
case 's':
case 'S':
return 4;
case 'c':
case 'C':
return 6;
case 'a':
case 'A':
return 7;
}
return 0;
}
OE_document* OE_svgParser::fromFile(const std::string filename, const char* units, float dpi)
OE_svgParser parser;
// Loading of the SVG file
TiXmlDocument doc(filename.c_str());
if (! doc.LoadFile())
{
std::cerr << "error during loading of the following file :" << std::endl;
std::cerr << "error #" << doc.ErrorId() << " : " << doc.ErrorDesc() << std::endl;
return nullptr;
}
TiXmlHandle hdl(&doc);
TiXmlElement *object = hdl.FirstChildElement("svg").Element();
parser.document = new OE_document();
if (parser.document)
{
parser.parseSvg(object);
}
return parser.document;
bool OE_svgParser::parseSvg(TiXmlElement *input)
TiXmlElement* element = 0;
if (input)
{
for( element = input->FirstChildElement(); element; element = element->NextSiblingElement() )
{
const char* val = element->Value();
if((strlen(val)==1)&&(strncmp(val,"g",1) == 0))
{
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
const char* transform = element->Attribute("transform");
OE_Matrix newTransform;
OE_Matrix oldTransform(matrix);
if (transform)
{
char snb[64];
float f[6] = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
if (!strncmp(transform, "matrix", 6))
{
const char* matrix = transform+strlen("matrix(");
for (int i=0; i<6; i++)
{
snb[0] = 0;
matrix = parseNumber(matrix, snb, sizeof(snb))+1;
f[i] = atof(snb);
}
newTransform = OE_Matrix(f);
}
else if (!strncmp(transform, "translate", 9))
{
const char* translate = transform+strlen("translate(");
for (int i=4; i<6; i++)
{
snb[0] = 0;
translate = parseNumber(translate, snb, sizeof(snb))+1;
f[i] = atof(snb);
}
}
else if (!strncmp(transform, "scale", 5))
{
const char* scale = transform+strlen("scale(");
for (int i=0; i<6; i+=3)
{
snb[0] = 0;
scale = parseNumber(scale, snb, sizeof(snb))+1;
f[i] = atof(snb);
}
}
else if (!strncmp(transform, "rotate", 6))
{
// https://developer.mozilla.org/en/docs/Web/SVG/Attribute/transform
printf("Unmanaged transform \"%s\"\n", transform);
}
else if (!strncmp(transform, "skewX", 5))
{
printf("Unmanaged transform \"%s\"\n", transform);
}
else if (!strncmp(transform, "skewX", 5))
{
printf("Unmanaged transform \"%s\"\n", transform);
}
else
{
printf("Unknown transform \"%s\"\n", transform);
}
}
matrix = matrix * newTransform;
if((strlen(val)==4)&&(strncmp(val,"path",4) == 0))
{
parsePath(element);
}
}
}
return true;
}
bool OE_svgParser::parsePath(TiXmlElement *input)
{
const char * s = input->Attribute("d");
char cmd = '\0';
float args[10];
int nargs;
int rargs = 0; /*nb arg after a command*/
float cpx, cpy, cpx2, cpy2;
char item[64];
//const char** attr;
//const char* s = NULL;
// const char* tmp[4];
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);
}
}*/
curve = new OE_pointcurve();
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
if (curve)
{
cpx = 0; cpy = 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':
pathMoveTo(curve, &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 = getArgsPerElement(cmd);
cpx2 = cpx; cpy2 = cpy;
break;
case 'l':
case 'L':
pathLineTo(curve, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
case 'H':
case 'h':
pathHLineTo(curve, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
case 'V':
case 'v':
pathVLineTo(curve, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
case 'C':
case 'c':
pathCubicBezTo(curve, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
break;
case 'S':
case 's':
pathCubicBezShortTo(curve, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
break;
case 'Q':
case 'q':
pathQuadBezTo(curve, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
break;
case 'T':
case 't':
pathQuadBezShortTo(curve, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
break;
case 'A':
case 'a':
pathArcTo(curve, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
default:
if (nargs >= 2)
{
cpx = args[nargs-2];
cpy = args[nargs-1];
cpx2 = cpx; cpy2 = cpy;
}
break;
}
nargs = 0;
}
}
else
{
cmd = item[0];
rargs = getArgsPerElement(cmd); //give us the number of arguments
if (cmd == 'M' || cmd == 'm')
{
// Add the curve to document.
pushCurve(curve);
// Start new curve.
curve = new OE_pointcurve();
nargs = 0;
}
else if (cmd == 'Z' || cmd == 'z')
{
curve->setClosed(true);
if (curve->getNpts() > 3)
{
// Move current point to first point
curve->getPoint(1,&cpx,&cpy);
cpx2 = cpx; cpy2 = cpy;
// Add the curve to document.
pushCurve(curve);
// Start new curve.
curve = new OE_pointcurve();
bool OE_svgParser::pushCurve(OE_pointcurve * curve)
return document->addCurve(curve);
delete curve;
curve = nullptr;
return false;