#define _CRT_SECURE_NO_WARNINGS #include "shape-description.h" namespace msdfgen { int readCharF(FILE *input) { int c = '\0'; do { c = fgetc(input); } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); return c; } int readCharS(const char **input) { int c = '\0'; do { c = *(*input)++; } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); if (!c) { --c; return EOF; } return c; } int readCoordF(FILE *input, Point2 &coord) { return fscanf(input, "%lf,%lf", &coord.x, &coord.y); } int readCoordS(const char **input, Point2 &coord) { int read = 0; int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read); *input += read; return result; } static bool writeCoord(FILE *output, Point2 coord) { fprintf(output, "%.12g, %.12g", coord.x, coord.y); return true; } template static int readControlPoints(T *input, Point2 *output) { int result = readCoord(input, output[0]); if (result == 2) { switch (readChar(input)) { case ')': return 1; case ';': break; default: return -1; } result = readCoord(input, output[1]); if (result == 2 && readChar(input) == ')') return 2; } else if (result != 1 && readChar(input) == ')') return 0; return -1; } template static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &colorsSpecified) { Point2 p[4], start; if (first) p[0] = *first; else { int result = readCoord(input, p[0]); if (result != 2) return result != 1 && readChar(input) == terminator; } start = p[0]; int c = '\0'; while ((c = readChar(input)) != terminator) { if (c != ';') return false; EdgeColor color = WHITE; int result = readCoord(input, p[1]); if (result == 2) { output.addEdge(EdgeHolder(p[0], p[1], color)); p[0] = p[1]; continue; } else if (result == 1) return false; else { int controlPoints = 0; switch ((c = readChar(input))) { case '#': output.addEdge(EdgeHolder(p[0], start, color)); p[0] = start; continue; case ';': goto FINISH_EDGE; case '(': goto READ_CONTROL_POINTS; case 'C': case 'c': color = CYAN; colorsSpecified = true; break; case 'M': case 'm': color = MAGENTA; colorsSpecified = true; break; case 'Y': case 'y': color = YELLOW; colorsSpecified = true; break; case 'W': case 'w': color = WHITE; colorsSpecified = true; break; default: return c == terminator; } switch (readChar(input)) { case ';': goto FINISH_EDGE; case '(': READ_CONTROL_POINTS: if ((controlPoints = readControlPoints(input, p+1)) < 0) return false; break; default: return false; } if (readChar(input) != ';') return false; FINISH_EDGE: result = readCoord(input, p[1+controlPoints]); if (result != 2) { if (result == 1) return false; else { if (readChar(input) == '#') p[1+controlPoints] = start; else return false; } } switch (controlPoints) { case 0: output.addEdge(EdgeHolder(p[0], p[1], color)); p[0] = p[1]; continue; case 1: output.addEdge(EdgeHolder(p[0], p[1], p[2], color)); p[0] = p[2]; continue; case 2: output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color)); p[0] = p[3]; continue; } } } return true; } bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) { bool locColorsSpec = false; output.contours.clear(); output.inverseYAxis = false; Point2 p; int result = readCoordF(input, p); if (result == 2) { return readContour(input, output.addContour(), &p, EOF, locColorsSpec); } else if (result == 1) return false; else { int c = readCharF(input); if (c == '@') { char after = '\0'; if (fscanf(input, "invert-y%c", &after) != 1) return feof(input) != 0; output.inverseYAxis = true; c = after; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') c = readCharF(input); } for (; c == '{'; c = readCharF(input)) if (!readContour(input, output.addContour(), NULL, '}', locColorsSpec)) return false; if (colorsSpecified) *colorsSpecified = locColorsSpec; return c == EOF && feof(input); } } bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) { bool locColorsSpec = false; output.contours.clear(); output.inverseYAxis = false; Point2 p; int result = readCoordS(&input, p); if (result == 2) { return readContour(&input, output.addContour(), &p, EOF, locColorsSpec); } else if (result == 1) return false; else { int c = readCharS(&input); if (c == '@') { for (int i = 0; i < (int) sizeof("invert-y")-1; ++i) if (input[i] != "invert-y"[i]) return false; output.inverseYAxis = true; input += sizeof("invert-y")-1; c = readCharS(&input); } for (; c == '{'; c = readCharS(&input)) if (!readContour(&input, output.addContour(), NULL, '}', locColorsSpec)) return false; if (colorsSpecified) *colorsSpecified = locColorsSpec; return c == EOF; } } static bool isColored(const Shape &shape) { for (std::vector::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) if ((*edge)->color != WHITE) return true; return false; } bool writeShapeDescription(FILE *output, const Shape &shape) { if (!shape.validate()) return false; bool writeColors = isColored(shape); if (shape.inverseYAxis) fprintf(output, "@invert-y\n"); for (std::vector::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { fprintf(output, "{\n"); if (!contour->edges.empty()) { for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { char colorCode = '\0'; if (writeColors) { switch ((*edge)->color) { case YELLOW: colorCode = 'y'; break; case MAGENTA: colorCode = 'm'; break; case CYAN: colorCode = 'c'; break; case WHITE: colorCode = 'w'; break; default:; } } const Point2 *p = (*edge)->controlPoints(); switch ((*edge)->type()) { case (int) LinearSegment::EDGE_TYPE: fprintf(output, "\t"); writeCoord(output, p[0]); fprintf(output, ";\n"); if (colorCode) fprintf(output, "\t\t%c;\n", colorCode); break; case (int) QuadraticSegment::EDGE_TYPE: fprintf(output, "\t"); writeCoord(output, p[0]); fprintf(output, ";\n\t\t"); if (colorCode) fprintf(output, "%c", colorCode); fprintf(output, "("); writeCoord(output, p[1]); fprintf(output, ");\n"); break; case (int) CubicSegment::EDGE_TYPE: fprintf(output, "\t"); writeCoord(output, p[0]); fprintf(output, ";\n\t\t"); if (colorCode) fprintf(output, "%c", colorCode); fprintf(output, "("); writeCoord(output, p[1]); fprintf(output, "; "); writeCoord(output, p[2]); fprintf(output, ");\n"); break; } } fprintf(output, "\t#\n"); } fprintf(output, "}\n"); } return true; } }