From 28ee740a33abf5805b899a1cb4d2e315e46231de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Chlumsk=C3=BD?= Date: Sun, 23 Jul 2017 20:04:10 +0200 Subject: [PATCH] SVG importer fixes: S, T curve commands, surplus commas --- ext/import-svg.cpp | 57 ++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/ext/import-svg.cpp b/ext/import-svg.cpp index 1d5467b..dd3462f 100644 --- a/ext/import-svg.cpp +++ b/ext/import-svg.cpp @@ -21,11 +21,16 @@ namespace msdfgen { #define REQUIRE(cond) { if (!(cond)) return false; } #endif +static void skipExtraChars(const char *&pathDef) { + while (*pathDef == ',' || *pathDef == ' ' || *pathDef == '\t' || *pathDef == '\r' || *pathDef == '\n') + ++pathDef; +} + static bool readNodeType(char &output, const char *&pathDef) { - int shift; - char nodeType; - if (sscanf(pathDef, " %c%n", &nodeType, &shift) == 1 && nodeType != '+' && nodeType != '-' && nodeType != '.' && nodeType != ',' && (nodeType < '0' || nodeType > '9')) { - pathDef += shift; + skipExtraChars(pathDef); + char nodeType = *pathDef; + if (nodeType && nodeType != '+' && nodeType != '-' && nodeType != '.' && nodeType != ',' && (nodeType < '0' || nodeType > '9')) { + ++pathDef; output = nodeType; return true; } @@ -33,9 +38,10 @@ static bool readNodeType(char &output, const char *&pathDef) { } static bool readCoord(Point2 &output, const char *&pathDef) { + skipExtraChars(pathDef); int shift; double x, y; - if (sscanf(pathDef, " %lf%lf%n", &x, &y, &shift) == 2 || sscanf(pathDef, " %lf , %lf%n", &x, &y, &shift) == 2) { + if (sscanf(pathDef, "%lf%lf%n", &x, &y, &shift) == 2 || sscanf(pathDef, "%lf , %lf%n", &x, &y, &shift) == 2) { output.x = x; output.y = y; pathDef += shift; @@ -45,9 +51,10 @@ static bool readCoord(Point2 &output, const char *&pathDef) { } static bool readDouble(double &output, const char *&pathDef) { + skipExtraChars(pathDef); int shift; double v; - if (sscanf(pathDef, " %lf%n", &v, &shift) == 1) { + if (sscanf(pathDef, "%lf%n", &v, &shift) == 1) { pathDef += shift; output = v; return true; @@ -56,9 +63,10 @@ static bool readDouble(double &output, const char *&pathDef) { } static bool readBool(bool &output, const char *&pathDef) { + skipExtraChars(pathDef); int shift; int v; - if (sscanf(pathDef, " %d%n", &v, &shift) == 1) { + if (sscanf(pathDef, "%d%n", &v, &shift) == 1) { pathDef += shift; output = v != 0; return true; @@ -66,17 +74,6 @@ static bool readBool(bool &output, const char *&pathDef) { return false; } -static void consumeWhitespace(const char *&pathDef) { - while (*pathDef == ' ' || *pathDef == '\t' || *pathDef == '\r' || *pathDef == '\n') - ++pathDef; -} - -static void consumeOptionalComma(const char *&pathDef) { - consumeWhitespace(pathDef); - if (*pathDef == ',') - ++pathDef; -} - static double arcAngle(Vector2 u, Vector2 v) { return nonZeroSign(crossProduct(u, v))*acos(clamp(dotProduct(u, v)/(u.length()*v.length()), -1., +1.)); } @@ -136,7 +133,8 @@ static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoi } static bool buildFromPath(Shape &shape, const char *pathDef, double size) { - char nodeType; + char nodeType = '\0'; + char prevNodeType = '\0'; Point2 prevNode(0, 0); bool nodeTypePreread = false; while (nodeTypePreread || readNodeType(nodeType, pathDef)) { @@ -184,7 +182,6 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { break; case 'Q': case 'q': REQUIRE(readCoord(controlPoint[0], pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readCoord(node, pathDef)); if (nodeType == 'q') { controlPoint[0] += prevNode; @@ -193,7 +190,10 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { contour.addEdge(new QuadraticSegment(prevNode, controlPoint[0], node)); break; case 'T': case 't': - controlPoint[0] = node+node-controlPoint[0]; + if (prevNodeType == 'Q' || prevNodeType == 'q' || prevNodeType == 'T' || prevNodeType == 't') + controlPoint[0] = node+node-controlPoint[0]; + else + controlPoint[0] = node; REQUIRE(readCoord(node, pathDef)); if (nodeType == 't') node += prevNode; @@ -201,9 +201,7 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { break; case 'C': case 'c': REQUIRE(readCoord(controlPoint[0], pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readCoord(controlPoint[1], pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readCoord(node, pathDef)); if (nodeType == 'c') { controlPoint[0] += prevNode; @@ -213,9 +211,11 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node)); break; case 'S': case 's': - controlPoint[0] = node+node-controlPoint[1]; + if (prevNodeType == 'C' || prevNodeType == 'c' || prevNodeType == 'S' || prevNodeType == 's') + controlPoint[0] = node+node-controlPoint[1]; + else + controlPoint[0] = node; REQUIRE(readCoord(controlPoint[1], pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readCoord(node, pathDef)); if (nodeType == 's') { controlPoint[1] += prevNode; @@ -230,13 +230,9 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { bool largeArg; bool sweep; REQUIRE(readCoord(radius, pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readDouble(angle, pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readBool(largeArg, pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readBool(sweep, pathDef)); - consumeOptionalComma(pathDef); REQUIRE(readCoord(node, pathDef)); if (nodeType == 'a') node += prevNode; @@ -249,8 +245,8 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { } contourStart &= nodeType == 'M' || nodeType == 'm'; prevNode = node; + prevNodeType = nodeType; readNodeType(nodeType, pathDef); - consumeWhitespace(pathDef); } NEXT_CONTOUR: // Fix contour if it isn't properly closed @@ -261,6 +257,7 @@ static bool buildFromPath(Shape &shape, const char *pathDef, double size) { contour.addEdge(new LinearSegment(prevNode, startPoint)); } prevNode = startPoint; + prevNodeType = '\0'; } return true; }