1
2#define _CRT_SECURE_NO_WARNINGS
3#include "shape-description.h"
4
5namespace msdfgen {
6
7int readCharF(FILE *input) {
8 int c = '\0';
9 do {
10 c = fgetc(input);
11 } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
12 return c;
13}
14
15int readCharS(const char **input) {
16 int c = '\0';
17 do {
18 c = *(*input)++;
19 } while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
20 if (!c) {
21 --c;
22 return EOF;
23 }
24 return c;
25}
26
27int readCoordF(FILE *input, Point2 &coord) {
28 return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
29}
30
31int readCoordS(const char **input, Point2 &coord) {
32 int read = 0;
33 int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
34 *input += read;
35 return result;
36}
37
38static bool writeCoord(FILE *output, Point2 coord) {
39 fprintf(output, "%.12g, %.12g", coord.x, coord.y);
40 return true;
41}
42
43template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
44static int readControlPoints(T *input, Point2 *output) {
45 int result = readCoord(input, output[0]);
46 if (result == 2) {
47 switch (readChar(input)) {
48 case ')':
49 return 1;
50 case ';':
51 break;
52 default:
53 return -1;
54 }
55 result = readCoord(input, output[1]);
56 if (result == 2 && readChar(input) == ')')
57 return 2;
58 } else if (result != 1 && readChar(input) == ')')
59 return 0;
60 return -1;
61}
62
63template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
64static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &colorsSpecified) {
65 Point2 p[4], start;
66 if (first)
67 p[0] = *first;
68 else {
69 int result = readCoord(input, p[0]);
70 if (result != 2)
71 return result != 1 && readChar(input) == terminator;
72 }
73 start = p[0];
74 int c = '\0';
75 while ((c = readChar(input)) != terminator) {
76 if (c != ';')
77 return false;
78 EdgeColor color = WHITE;
79 int result = readCoord(input, p[1]);
80 if (result == 2) {
81 output.addEdge(EdgeHolder(p[0], p[1], color));
82 p[0] = p[1];
83 continue;
84 } else if (result == 1)
85 return false;
86 else {
87 int controlPoints = 0;
88 switch ((c = readChar(input))) {
89 case '#':
90 output.addEdge(EdgeHolder(p[0], start, color));
91 p[0] = start;
92 continue;
93 case ';':
94 goto FINISH_EDGE;
95 case '(':
96 goto READ_CONTROL_POINTS;
97 case 'C': case 'c':
98 color = CYAN;
99 colorsSpecified = true;
100 break;
101 case 'M': case 'm':
102 color = MAGENTA;
103 colorsSpecified = true;
104 break;
105 case 'Y': case 'y':
106 color = YELLOW;
107 colorsSpecified = true;
108 break;
109 case 'W': case 'w':
110 color = WHITE;
111 colorsSpecified = true;
112 break;
113 default:
114 return c == terminator;
115 }
116 switch (readChar(input)) {
117 case ';':
118 goto FINISH_EDGE;
119 case '(':
120 READ_CONTROL_POINTS:
121 if ((controlPoints = readControlPoints<T, readChar, readCoord>(input, p+1)) < 0)
122 return false;
123 break;
124 default:
125 return false;
126 }
127 if (readChar(input) != ';')
128 return false;
129 FINISH_EDGE:
130 result = readCoord(input, p[1+controlPoints]);
131 if (result != 2) {
132 if (result == 1)
133 return false;
134 else {
135 if (readChar(input) == '#')
136 p[1+controlPoints] = start;
137 else
138 return false;
139 }
140 }
141 switch (controlPoints) {
142 case 0:
143 output.addEdge(EdgeHolder(p[0], p[1], color));
144 p[0] = p[1];
145 continue;
146 case 1:
147 output.addEdge(EdgeHolder(p[0], p[1], p[2], color));
148 p[0] = p[2];
149 continue;
150 case 2:
151 output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color));
152 p[0] = p[3];
153 continue;
154 }
155 }
156 }
157 return true;
158}
159
160bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
161 bool locColorsSpec = false;
162 output.contours.clear();
163 output.inverseYAxis = false;
164 Point2 p;
165 int result = readCoordF(input, p);
166 if (result == 2) {
167 return readContour<FILE, readCharF, readCoordF>(input, output.addContour(), &p, EOF, locColorsSpec);
168 } else if (result == 1)
169 return false;
170 else {
171 int c = readCharF(input);
172 if (c == '@') {
173 char after = '\0';
174 if (fscanf(input, "invert-y%c", &after) != 1)
175 return feof(input) != 0;
176 output.inverseYAxis = true;
177 c = after;
178 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
179 c = readCharF(input);
180 }
181 for (; c == '{'; c = readCharF(input))
182 if (!readContour<FILE, readCharF, readCoordF>(input, output.addContour(), NULL, '}', locColorsSpec))
183 return false;
184 if (colorsSpecified)
185 *colorsSpecified = locColorsSpec;
186 return c == EOF && feof(input);
187 }
188}
189
190bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
191 bool locColorsSpec = false;
192 output.contours.clear();
193 output.inverseYAxis = false;
194 Point2 p;
195 int result = readCoordS(&input, p);
196 if (result == 2) {
197 return readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), &p, EOF, locColorsSpec);
198 } else if (result == 1)
199 return false;
200 else {
201 int c = readCharS(&input);
202 if (c == '@') {
203 for (int i = 0; i < (int) sizeof("invert-y")-1; ++i)
204 if (input[i] != "invert-y"[i])
205 return false;
206 output.inverseYAxis = true;
207 input += sizeof("invert-y")-1;
208 c = readCharS(&input);
209 }
210 for (; c == '{'; c = readCharS(&input))
211 if (!readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), NULL, '}', locColorsSpec))
212 return false;
213 if (colorsSpecified)
214 *colorsSpecified = locColorsSpec;
215 return c == EOF;
216 }
217}
218
219static bool isColored(const Shape &shape) {
220 for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
221 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
222 if ((*edge)->color != WHITE)
223 return true;
224 return false;
225}
226
227bool writeShapeDescription(FILE *output, const Shape &shape) {
228 if (!shape.validate())
229 return false;
230 bool writeColors = isColored(shape);
231 if (shape.inverseYAxis)
232 fprintf(output, "@invert-y\n");
233 for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
234 fprintf(output, "{\n");
235 if (!contour->edges.empty()) {
236 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
237 char colorCode = '\0';
238 if (writeColors) {
239 switch ((*edge)->color) {
240 case YELLOW: colorCode = 'y'; break;
241 case MAGENTA: colorCode = 'm'; break;
242 case CYAN: colorCode = 'c'; break;
243 case WHITE: colorCode = 'w'; break;
244 default:;
245 }
246 }
247 if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) {
248 fprintf(output, "\t");
249 writeCoord(output, e->p[0]);
250 fprintf(output, ";\n");
251 if (colorCode)
252 fprintf(output, "\t\t%c;\n", colorCode);
253 }
254 if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) {
255 fprintf(output, "\t");
256 writeCoord(output, e->p[0]);
257 fprintf(output, ";\n\t\t");
258 if (colorCode)
259 fprintf(output, "%c", colorCode);
260 fprintf(output, "(");
261 writeCoord(output, e->p[1]);
262 fprintf(output, ");\n");
263 }
264 if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) {
265 fprintf(output, "\t");
266 writeCoord(output, e->p[0]);
267 fprintf(output, ";\n\t\t");
268 if (colorCode)
269 fprintf(output, "%c", colorCode);
270 fprintf(output, "(");
271 writeCoord(output, e->p[1]);
272 fprintf(output, "; ");
273 writeCoord(output, e->p[2]);
274 fprintf(output, ");\n");
275 }
276 }
277 fprintf(output, "\t#\n");
278 }
279 fprintf(output, "}\n");
280 }
281 return true;
282}
283
284}
285