├── .gitignore
├── screenshots
└── 0001.png
├── Makefile
├── examples
├── example.c
├── urls.txt
└── hershey_text.c
├── README.md
└── svg2pl.c
/.gitignore:
--------------------------------------------------------------------------------
1 | tests
2 | .DS_Store
3 | */.DS_Store
4 | **/.DS_Store
5 | svg2pl
--------------------------------------------------------------------------------
/screenshots/0001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LingDong-/svg2pl/HEAD/screenshots/0001.png
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | cmd:
2 | gcc -Wall -O3 examples/example.c -o svg2pl
3 | cmdt:
4 | gcc -Wall -O3 examples/hershey_text.c -o svg2pl
5 | testurl: cmd
6 | curl -s $(FILE) | ./svg2pl > out.svg
7 | test1: cmd
8 | ./svg2pl $(FILE) > $(FILE).o.svg
9 | dltest:
10 | mkdir -p tests;\
11 | while read -r line; do \
12 | curl -s https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/$$line > tests/$$line; \
13 | done < examples/urls.txt;
14 | testfold: cmd
15 | while read -r line; do \
16 | ./svg2pl tests/$$line > tests/$$line.o.svg; \
17 | done < examples/urls.txt;
--------------------------------------------------------------------------------
/examples/example.c:
--------------------------------------------------------------------------------
1 | #define S2P_SETDIM(w,h) {printf("");
26 |
27 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # svg2pl
2 |
3 | *a tiny library to convert (reasonable subset of) svg to polylines*
4 |
5 | - uses very little memory, 0 dynamic allocations
6 | - single file, no dependencies other than C
7 | - extremely fast
8 | - no error checking -- assumes svg is perfectly valid, otherwise UB
9 | - intended for compact projects or embeded systems
10 |
11 | 
12 |
13 | ## supported svg features:
14 |
15 | - transform attribute on all elements
16 | - all path d= commands (beziers, arcs...)
17 | - elements: g, path, line, polyline, polygon, circle, ellipse, rect, svg
18 | - you can register custom parser functions for other tags (see usage)
19 |
20 | ## limitations:
21 |
22 | - curves and ellipses are discretized
23 | - fills and other styles are ignored,
24 | you can choose whether to skip all elements without "stoke" attribute
25 | - viewBox and nested svg are supported,
26 | but preserveAspectRatio can only be "xMidYMid meet"(default) or "none"
27 | - percentage(%) and physical units are not supported
28 | - JS, CSS and other crazy stuff are obviously not supported
29 |
30 | ## usage:
31 | 1. first define macros `S2P_MOVETO(x,y)` and `S2P_LINETO(x,y)`
32 | - these define what to do when the library has extracted polyline vertices
33 | - you can write to your own data structure, print, or control a machine, etc.
34 | - when not overwritten, these macros by default print the vertices to stdout.
35 | 2. (optional) define macro `S2P_SETDIM(w,h)`
36 | - this define what to do when the width/height of the svg are extracted
37 | - e.g. you might scale all subsequent vertices
38 | 3. `#include "svg2pl.c"`
39 | 4. (optional) call `s2p_def_tag` to register parsers for custom tags (e.g. `
47 | #include
48 | #include
49 |
50 | #define S2P_TAG_OPEN 0
51 | #define S2P_TAG_SELFCL 1
52 | #define S2P_TAG_CLOSE 2
53 |
54 | #define S2P__ST_OUT 0
55 | #define S2P__ST_OPEN 1
56 | #define S2P__ST_TAG 2
57 | #define S2P__ST_IN 3
58 | #define S2P__ST_AK 4
59 | #define S2P__ST_AKE 5
60 | #define S2P__ST_AVQ 6
61 | #define S2P__ST_AV 7
62 | #define S2P__ST_EXCL 8
63 | #define S2P__ST_CMMT 9
64 |
65 | #define S2P_MAX_ATTR 32
66 | #define S2P_MAX_AK_LEN 7
67 | #define S2P_MAX_TAG_LEN 7
68 |
69 | #define S2P_MAX_MAT 32
70 |
71 | #define S2P_MAX_CUSTFUN 16
72 |
73 | #define S2P__MAX(a,b) ((a) > (b) ? (a) : (b))
74 | #define S2P__MIN(a,b) ((a) < (b) ? (a) : (b))
75 |
76 | #ifndef S2P_MOVETO
77 | #define S2P_MOVETO(x,y) {printf("M %f %f\n",(x),(y));}
78 | #endif
79 |
80 | #ifndef S2P_LINETO
81 | #define S2P_LINETO(x,y) {printf("L %f %f\n",x,y);}
82 | #endif
83 |
84 | #ifndef S2P_SETDIM
85 | #define S2P_SETDIM(w,h) {printf("M 0 0 h %f v %f h %f z\n",(w),(h),-(w));}
86 | #endif
87 |
88 | int s2p_reso_curv = 24;
89 | int s2p_reso_circ = 24;
90 | int s2p_skip_nostroke = 0;
91 |
92 | typedef struct s2p_attr_st {
93 | int k_len;
94 | int v_len;
95 | long k;
96 | long v;
97 | } s2p_attr_t;
98 |
99 | typedef void (* s2p_tagdef_funptr_t) (FILE* fd, s2p_attr_t* attrs, int n_attr);
100 |
101 | typedef struct s2p_tagdef_st{
102 | char type;
103 | char name[S2P_MAX_TAG_LEN];
104 | s2p_tagdef_funptr_t func;
105 | } s2p_tagdef_t;
106 |
107 | s2p_tagdef_t s2p__tagdefs[S2P_MAX_CUSTFUN];
108 | int s2p__ntd = 0;
109 |
110 | float s2p__mats[S2P_MAX_MAT][9];
111 | int s2p__mati = 0;
112 |
113 | #define S2P__MATSET(A,a,b,c,d,e,f) {A[0]=a;A[1]=b;A[2]=c;A[3]=d;A[4]=e;A[5]=f;A[6]=0;A[7]=0;A[8]=1;}
114 |
115 | void s2p__matmul(float* A, float* B, float* C){
116 | float a = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
117 | float b = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
118 | float c = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];
119 |
120 | float d = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
121 | float e = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
122 | float f = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];
123 |
124 | float g = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
125 | float h = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
126 | float i = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];
127 |
128 | C[0] = a; C[1] = b; C[2] = c;
129 | C[3] = d; C[4] = e; C[5] = f;
130 | C[6] = g; C[7] = h; C[8] = i;
131 | }
132 |
133 | void s2p__m_moveto(float x, float y){
134 | float* m = s2p__mats[s2p__mati];
135 | float x1 = m[0] * x + m[1] * y + m[2];
136 | float y1 = m[3] * x + m[4] * y + m[5];
137 | float z1 = m[6] * x + m[7] * y + m[8];
138 | S2P_MOVETO(x1/z1,y1/z1);
139 | }
140 |
141 | void s2p__m_lineto(float x, float y){
142 | float* m = s2p__mats[s2p__mati];
143 | float x1 = m[0] * x + m[1] * y + m[2];
144 | float y1 = m[3] * x + m[4] * y + m[5];
145 | float z1 = m[6] * x + m[7] * y + m[8];
146 | S2P_LINETO(x1/z1,y1/z1);
147 | }
148 |
149 |
150 | void s2p_quadratic_bezier(
151 | float x0,float y0,float x1,float y1,
152 | float x2,float y2,
153 | float t, float* xo, float* yo){
154 | float s = 1-t;
155 | float s2 = s*s;
156 | float t2 = t*t;
157 | (*xo) = s2*x0+2*s*t*x1+t2*x2;
158 | (*yo) = s2*y0+2*s*t*y1+t2*y2;
159 | }
160 |
161 | void s2p_cubic_bezier(
162 | float x0,float y0,float x1,float y1,
163 | float x2,float y2,float x3,float y3,
164 | float t, float* xo, float* yo){
165 | float s = 1-t;
166 | float s2 = s*s;
167 | float s3 = s*s2;
168 | float t2 = t*t;
169 | float t3 = t2*t;
170 | (*xo) = s3*x0+3*s2*t*x1+3*s*t2*x2+t3*x3;
171 | (*yo) = s3*y0+3*s2*t*y1+3*s*t2*y2+t3*y3;
172 | }
173 |
174 |
175 | float s2p__aang(float ux, float uy, float vx, float vy){
176 | float dot = ux*vx+uy*vy;
177 | float den = sqrt(ux*ux+uy*uy)*sqrt(vx*vx+vy*vy);
178 | float a = acos(dot/den);
179 | float c = ux*vy-uy*vx;
180 | if (c < 0){
181 | a *= -1;
182 | }
183 | return a;
184 | }
185 |
186 | void s2p_arc(
187 | float x1, float y1, float x2, float y2,
188 | int fa, int fs,
189 | float rx, float ry, float phi){
190 | //https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
191 |
192 | float x125 = (x1-x2)*0.5;
193 | float y125 = (y1-y2)*0.5;
194 | float cosph = cos(phi);
195 | float sinph = sin(phi);
196 |
197 | float x1p = cosph*x125+sinph*y125;
198 | float y1p =-sinph*x125+cosph*y125;
199 | float x1p2 = x1p*x1p;
200 | float y1p2 = y1p*y1p;
201 |
202 | if (rx == 0 || ry == 0){
203 | return s2p__m_lineto(x2,y2);
204 | }
205 | if (rx < 0) rx = -rx;
206 | if (ry < 0) ry = -ry;
207 |
208 | float lam = x1p2/(rx*rx) + y1p2/(ry*ry);
209 | if (lam >= 1.0){
210 | float sql = sqrt(lam);
211 | rx = sql*rx;
212 | ry = sql*ry;
213 | }
214 |
215 | float rx2 = rx*rx;
216 | float ry2 = ry*ry;
217 |
218 | float rr = (rx2*y1p2+ry2*x1p2);
219 | float rrr = fabs(rx2*ry2-rr); //or fmax(rx2*ry2-rr,0)??
220 | float sqrrr = sqrt(rrr/rr);
221 |
222 | float cxp = sqrrr * (rx*y1p) / ry;
223 | float cyp =-sqrrr * (ry*x1p) / rx;
224 | if (fa == fs){
225 | cxp *= -1;
226 | cyp *= -1;
227 | }
228 |
229 | float cx = cosph*cxp - sinph*cyp + (x1+x2)*0.5;
230 | float cy = sinph*cxp + cosph*cyp + (y1+y2)*0.5;
231 | float xpcr = (x1p-cxp)/rx;
232 | float ypcr = (y1p-cyp)/ry;
233 |
234 | float th1 = s2p__aang(1,0, xpcr, ypcr );
235 | float dth = s2p__aang(xpcr, ypcr, (-x1p-cxp)/rx, (-y1p-cyp)/ry);
236 | dth = fmod(dth+M_PI*4, M_PI*2);
237 | if (fs == 0){
238 | while (dth > 0){
239 | dth -= M_PI*2;
240 | }
241 | }else{
242 | while (dth < 0){
243 | dth += M_PI*2;
244 | }
245 | }
246 |
247 | for (int i = 0; i < s2p_reso_curv; i++){
248 | float t = (float)(i+1)/(float)s2p_reso_curv;
249 | float th = th1 + dth * t;
250 | float x0 = cos(th)*rx;
251 | float y0 = sin(th)*ry;
252 | float xx = cx+cosph*x0-sinph*y0;
253 | float yy = cy+sinph*x0+cosph*y0;
254 | s2p__m_lineto(xx,yy);
255 | }
256 |
257 | }
258 |
259 | int s2p__d_next(FILE* fd){
260 | char c;
261 | while ( c=fgetc(fd), c==','||c<=' '){}
262 | int is_n = '+' <= c && c <= '9';
263 | ungetc(c,fd);
264 | return is_n;
265 | }
266 |
267 |
268 | void s2p__parse_transf(FILE* fd, long v_pos, int v_len, float* mat){
269 | fseek(fd, v_pos, SEEK_SET);
270 |
271 | char c;
272 | float x0,x1,x2;
273 | float m [9] = {1,0,0,0,1,0,0,0,1};
274 | float m1[9] = {1,0,0,0,1,0,0,0,1};
275 |
276 | while (ftell(fd) < v_pos + v_len){
277 | s2p__d_next(fd);
278 | c = fgetc(fd);
279 | if (c == 't'){//ranslate
280 | fseek(fd,8,SEEK_CUR);
281 | s2p__d_next(fd);
282 | fgetc(fd);
283 | s2p__d_next(fd);
284 | fscanf(fd, "%f", &x0);
285 | s2p__d_next(fd);
286 | fscanf(fd, "%f", &x1);
287 | s2p__d_next(fd);
288 | fgetc(fd);
289 |
290 | S2P__MATSET(m, 1,0,x0, 0,1,x1);
291 | s2p__matmul(mat,m,mat);
292 | }else if (c == 'r'){//otate
293 | fseek(fd,5,SEEK_CUR);
294 | s2p__d_next(fd);
295 | fgetc(fd);
296 | s2p__d_next(fd);
297 | fscanf(fd, "%f", &x0);
298 | float th = x0 * M_PI / 180.0;
299 | S2P__MATSET(m, cos(th), -sin(th), 0, sin(th), cos(th), 0);
300 |
301 | if (s2p__d_next(fd)){
302 | fscanf(fd, "%f", &x1);
303 | s2p__d_next(fd);
304 | fscanf(fd, "%f", &x2);
305 | s2p__d_next(fd);
306 | m[2] = x1;
307 | m[5] = x2;
308 | S2P__MATSET(m1, 1,0,-x1, 0,1,-x2);
309 | s2p__matmul(m,m1,m);
310 | }
311 | s2p__matmul(mat,m,mat);
312 | fgetc(fd);
313 | }else if (c == 'm'){//atrix
314 | fseek(fd,5,SEEK_CUR);
315 | s2p__d_next(fd);
316 | fgetc(fd);
317 | s2p__d_next(fd);
318 |
319 | for (int i = 0; i < 6; i++){
320 | fscanf(fd, "%f", &x0);
321 | m[(i&1)*3+(i>>1)] = x0; //col maj
322 | s2p__d_next(fd);
323 | }
324 | m[6] = 0; m[7] = 0; m[8] = 1;
325 | fgetc(fd);
326 | s2p__matmul(mat,m,mat);
327 |
328 | }else if (c == 's'){
329 | fseek(fd,3,SEEK_CUR);
330 | c = fgetc(fd);
331 | if (c == 'e'){//scale
332 | s2p__d_next(fd);
333 | fgetc(fd);
334 | s2p__d_next(fd);
335 | fscanf(fd, "%f", &x0);
336 |
337 | S2P__MATSET(m, x0,0,0, 0,x0,0);
338 |
339 | if (s2p__d_next(fd)){
340 | fscanf(fd, "%f", &x1);
341 | s2p__d_next(fd);
342 |
343 | m[4] = x1;
344 | }
345 | s2p__matmul(mat,m,mat);
346 | fgetc(fd);
347 | }else if (c == 'X'){//skewX
348 | s2p__d_next(fd);
349 | fgetc(fd);
350 | s2p__d_next(fd);
351 | fscanf(fd, "%f", &x0);
352 | s2p__d_next(fd);
353 | fgetc(fd);
354 |
355 | S2P__MATSET(m, 1,tan(x0*M_PI/180.0),0, 0,1,0);
356 | s2p__matmul(mat,m,mat);
357 |
358 | }else if (c == 'Y'){//skewY
359 | s2p__d_next(fd);
360 | fgetc(fd);
361 | s2p__d_next(fd);
362 | fscanf(fd, "%f", &x0);
363 | s2p__d_next(fd);
364 | fgetc(fd);
365 |
366 | S2P__MATSET(m, 1,0,0, tan(x0*M_PI/180.0),1,0);
367 | s2p__matmul(mat,m,mat);
368 | }
369 |
370 | }
371 | }
372 | }
373 |
374 |
375 |
376 |
377 | void s2p__get_transf(FILE* fd, s2p_attr_t* attrs, int n_attr, float* mat){
378 | for (int i = 0; i < n_attr; i++){
379 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
380 | fseek(fd, attrs[i].k, SEEK_SET);
381 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
382 | if (!strncmp(keybuf,"tra",3)){
383 | s2p__parse_transf(fd,attrs[i].v,attrs[i].v_len,mat);
384 | return;
385 | }
386 | }
387 | }
388 |
389 | int s2p__get_stroke(FILE* fd, s2p_attr_t* attrs, int n_attr){
390 | for (int i = 0; i < n_attr; i++){
391 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
392 | fseek(fd, attrs[i].k, SEEK_SET);
393 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
394 |
395 | if (!strcmp(keybuf,"stroke")){
396 | fseek(fd, attrs[i].v, SEEK_SET);
397 | if (fgetc(fd) != 'n' || fgetc(fd) != 'o'){
398 | return 1;
399 | }else{
400 | return 0;
401 | }
402 | }
403 | }
404 | return 0;
405 | }
406 |
407 | void s2p__parse_line(FILE* fd, s2p_attr_t* attrs, int n_attr){
408 |
409 | float x1, y1, x2, y2;
410 | for (int i = 0; i < n_attr; i++){
411 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
412 | fseek(fd, attrs[i].k, SEEK_SET);
413 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
414 | if (!strcmp(keybuf,"x1")){
415 | fseek(fd, attrs[i].v, SEEK_SET);
416 | fscanf(fd, "%f", &x1);
417 | }else if (!strcmp(keybuf,"y1")){
418 | fseek(fd, attrs[i].v, SEEK_SET);
419 | fscanf(fd, "%f", &y1);
420 | }else if (!strcmp(keybuf,"x2")){
421 | fseek(fd, attrs[i].v, SEEK_SET);
422 | fscanf(fd, "%f", &x2);
423 | }else if (!strcmp(keybuf,"y2")){
424 | fseek(fd, attrs[i].v, SEEK_SET);
425 | fscanf(fd, "%f", &y2);
426 | }
427 | }
428 | s2p__m_moveto(x1,y1);
429 | s2p__m_lineto(x2,y2);
430 | }
431 |
432 |
433 | void s2p__parse_rect(FILE* fd, s2p_attr_t* attrs, int n_attr){
434 | float x=0,y=0,w,h,rx=0,ry=0;
435 | for (int i = 0; i < n_attr; i++){
436 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
437 | fseek(fd, attrs[i].k, SEEK_SET);
438 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
439 | if (!strcmp(keybuf,"x")){
440 | fseek(fd, attrs[i].v, SEEK_SET);
441 | fscanf(fd, "%f", &x);
442 | }else if (!strcmp(keybuf,"y")){
443 | fseek(fd, attrs[i].v, SEEK_SET);
444 | fscanf(fd, "%f", &y);
445 | }else if (!strcmp(keybuf,"width")){
446 | fseek(fd, attrs[i].v, SEEK_SET);
447 | fscanf(fd, "%f", &w);
448 | }else if (!strcmp(keybuf,"height")){
449 | fseek(fd, attrs[i].v, SEEK_SET);
450 | fscanf(fd, "%f", &h);
451 | }else if (!strcmp(keybuf,"rx")){
452 | fseek(fd, attrs[i].v, SEEK_SET);
453 | fscanf(fd, "%f", &rx);
454 | }else if (!strcmp(keybuf,"ry")){
455 | fseek(fd, attrs[i].v, SEEK_SET);
456 | fscanf(fd, "%f", &ry);
457 | }
458 | }
459 | if (rx && !ry){
460 | ry = rx;
461 | }
462 | if (!rx || !ry){
463 | s2p__m_moveto(x,y);
464 | s2p__m_lineto(x+w,y);
465 | s2p__m_lineto(x+w,y+h);
466 | s2p__m_lineto(x,y+h);
467 | s2p__m_lineto(x,y);
468 | }else{
469 | rx = fabs(rx);
470 | ry = fabs(ry);
471 | if (rx > w/2) rx = w/2;
472 | if (ry > h/2) ry = h/2;
473 | s2p__m_moveto(x,y+ry);
474 | s2p_arc(x,y+ry, x+rx,y, 0,1, rx,ry,0);
475 | s2p__m_lineto(x+w-rx,y);
476 | s2p_arc(x+w-rx,y, x+w,y+ry, 0,1, rx,ry,0);
477 | s2p__m_lineto(x+w,y+h-ry);
478 | s2p_arc(x+w,y+h-ry, x+w-rx,y+h, 0,1, rx,ry,0);
479 | s2p__m_lineto(x+rx,y+h);
480 | s2p_arc(x+rx,y+h, x,y+h-ry, 0,1, rx,ry,0);
481 | s2p__m_lineto(x,y+ry);
482 | }
483 | }
484 |
485 |
486 | void s2p__parse_ellipse(FILE* fd, s2p_attr_t* attrs, int n_attr){
487 | float cx=0,cy=0,rx=0,ry=0;
488 | for (int i = 0; i < n_attr; i++){
489 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
490 | fseek(fd, attrs[i].k, SEEK_SET);
491 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
492 | if (!strcmp(keybuf,"cx")){
493 | fseek(fd, attrs[i].v, SEEK_SET);
494 | fscanf(fd, "%f", &cx);
495 | }else if (!strcmp(keybuf,"cy")){
496 | fseek(fd, attrs[i].v, SEEK_SET);
497 | fscanf(fd, "%f", &cy);
498 | }else if (!strcmp(keybuf,"r")){
499 | fseek(fd, attrs[i].v, SEEK_SET);
500 | fscanf(fd, "%f", &rx);
501 | }else if (!strcmp(keybuf,"rx")){
502 | fseek(fd, attrs[i].v, SEEK_SET);
503 | fscanf(fd, "%f", &rx);
504 | }else if (!strcmp(keybuf,"ry")){
505 | fseek(fd, attrs[i].v, SEEK_SET);
506 | fscanf(fd, "%f", &ry);
507 | }
508 | }
509 | if (rx && !ry) ry = rx;
510 | if (ry && !rx) rx = ry;
511 | rx = fabs(rx);
512 | ry = fabs(ry);
513 | for (int i = 0; i < s2p_reso_circ; i++){
514 | float t = (float)i/(float)(s2p_reso_circ-1);
515 | float a = t * M_PI * 2;
516 | float x = cx+cos(a)*rx;
517 | float y = cy+sin(a)*ry;
518 | if (i){
519 | s2p__m_lineto(x,y);
520 | }else{
521 | s2p__m_moveto(x,y);
522 | }
523 | }
524 | }
525 |
526 | void s2p__parse_poly(FILE* fd, s2p_attr_t* attrs, int n_attr, int closed){
527 | for (int i = 0; i < n_attr; i++){
528 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
529 | fseek(fd, attrs[i].k, SEEK_SET);
530 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
531 | if (!strcmp(keybuf,"points")){
532 | fseek(fd, attrs[i].v, SEEK_SET);
533 | float x,y,x0,y0;
534 | int j=0;
535 | while (ftell(fd) < attrs[i].v + attrs[i].v_len){
536 | fscanf(fd, "%f", &x);
537 | fgetc(fd);
538 | fscanf(fd, "%f", &y);
539 | fgetc(fd);
540 | if (j){
541 | s2p__m_lineto(x,y);
542 | }else{
543 | s2p__m_moveto(x,y);
544 | x0 = x;
545 | y0 = y;
546 | }
547 | j++;
548 | }
549 | if (closed){
550 | s2p__m_lineto(x0,y0);
551 | }
552 | }
553 | }
554 | }
555 |
556 |
557 |
558 |
559 | void s2p__parse_d(FILE* fd, long v_pos, int v_len){
560 | fseek(fd, v_pos, SEEK_SET);
561 | char c;
562 | char lc;
563 | float lx=0, ly=0;
564 | float ox=0, oy=0;
565 | float cx=0, cy=0;
566 | float qx=0, qy=0;
567 | float x0,y0;
568 | float x1,y1;
569 | float x2,y2;
570 | int f0,f1;
571 | while (ftell(fd) < v_pos + v_len){
572 | s2p__d_next(fd);
573 | c = fgetc(fd);
574 | if (c < 'A'){
575 | ungetc(c,fd);
576 | c = lc;
577 | }
578 | if (c == 'M'){
579 | fscanf(fd, "%f", &x0);
580 | s2p__d_next(fd);
581 | fscanf(fd, "%f", &y0);
582 | s2p__m_moveto(ox=lx=x0,oy=ly=y0);
583 | while (s2p__d_next(fd)){
584 | fscanf(fd, "%f", &x0);
585 | s2p__d_next(fd);
586 | fscanf(fd, "%f", &y0);
587 | s2p__m_lineto(lx=x0,ly=y0);
588 | }
589 | }else if (c == 'm'){
590 | fscanf(fd, "%f", &x0);
591 | s2p__d_next(fd);
592 | fscanf(fd, "%f", &y0);
593 | s2p__m_moveto(ox=(lx+=x0),oy=(ly+=y0));
594 | while (s2p__d_next(fd)){
595 | fscanf(fd, "%f", &x0);
596 | s2p__d_next(fd);
597 | fscanf(fd, "%f", &y0);
598 | s2p__m_lineto(lx+=x0,ly+=y0);
599 | }
600 | }else if (c == 'L'){
601 | fscanf(fd, "%f", &x0);
602 | s2p__d_next(fd);
603 | fscanf(fd, "%f", &y0);
604 | s2p__m_lineto(lx=x0,ly=y0);
605 |
606 | }else if (c == 'l'){
607 | fscanf(fd, "%f", &x0);
608 | s2p__d_next(fd);
609 | fscanf(fd, "%f", &y0);
610 | s2p__m_lineto(lx+=x0,ly+=y0);
611 |
612 | }else if (c == 'H'){
613 | fscanf(fd, "%f", &x0);
614 | s2p__m_lineto(lx=x0,ly);
615 |
616 | }else if (c == 'h'){
617 | fscanf(fd, "%f", &x0);
618 | s2p__m_lineto(lx+=x0,ly);
619 |
620 | }else if (c == 'V'){
621 | fscanf(fd, "%f", &y0);
622 | s2p__m_lineto(lx,ly=y0);
623 |
624 | }else if (c == 'v'){
625 | fscanf(fd, "%f", &y0);
626 | s2p__m_lineto(lx,ly+=y0);
627 |
628 | }else if (c == 'Z' || c == 'z'){
629 | s2p__m_lineto(lx=ox,ly=oy);
630 | }else if (c == 'C' || c == 'c'){
631 | float _x = 0;
632 | float _y = 0;
633 | if (c == 'c'){
634 | _x = lx;
635 | _y = ly;
636 | }
637 | fscanf(fd, "%f", &x0);
638 | s2p__d_next(fd);
639 | fscanf(fd, "%f", &y0);
640 | s2p__d_next(fd);
641 | fscanf(fd, "%f", &x1);
642 | s2p__d_next(fd);
643 | fscanf(fd, "%f", &y1);
644 | s2p__d_next(fd);
645 | fscanf(fd, "%f", &x2);
646 | s2p__d_next(fd);
647 | fscanf(fd, "%f", &y2);
648 | for (int i = 0; i < s2p_reso_curv; i++){
649 | float t = (float)(i+1)/(float)(s2p_reso_curv);
650 | float xt,yt;
651 | s2p_cubic_bezier(lx,ly,_x+x0,_y+y0,_x+x1,_y+y1,_x+x2,_y+y2,t,&xt,&yt);
652 | s2p__m_lineto(xt,yt);
653 | }
654 | lx = _x+x2;
655 | ly = _y+y2;
656 | cx = _x+x1;
657 | cy = _y+y1;
658 | }else if (c == 'Q' || c == 'q'){
659 | float _x = 0;
660 | float _y = 0;
661 | if (c == 'q'){
662 | _x = lx;
663 | _y = ly;
664 | }
665 | fscanf(fd, "%f", &x0);
666 | s2p__d_next(fd);
667 | fscanf(fd, "%f", &y0);
668 | s2p__d_next(fd);
669 | fscanf(fd, "%f", &x1);
670 | s2p__d_next(fd);
671 | fscanf(fd, "%f", &y1);
672 | for (int i = 0; i < s2p_reso_curv; i++){
673 | float t = (float)(i+1)/(float)(s2p_reso_curv);
674 | float xt,yt;
675 | s2p_quadratic_bezier(lx,ly,_x+x0,_y+y0,_x+x1,_y+y1,t,&xt,&yt);
676 | s2p__m_lineto(xt,yt);
677 | }
678 | lx = _x+x1;
679 | ly = _y+y1;
680 | qx = _x+x0;
681 | qy = _y+y0;
682 | }else if (c == 'S' || c == 's'){
683 | float _x = 0, _y = 0;
684 | if (c == 's'){
685 | _x = lx;
686 | _y = ly;
687 | }
688 | if (!(lc == 'C' || lc == 'c' || lc == 'S' || lc == 's')){
689 | cx = lx;
690 | cy = ly;
691 | }
692 | x0 = (lx-cx)+lx;
693 | y0 = (ly-cy)+ly;
694 | fscanf(fd, "%f", &x1);
695 | s2p__d_next(fd);
696 | fscanf(fd, "%f", &y1);
697 | s2p__d_next(fd);
698 | fscanf(fd, "%f", &x2);
699 | s2p__d_next(fd);
700 | fscanf(fd, "%f", &y2);
701 | for (int i = 0; i < s2p_reso_curv; i++){
702 | float t = (float)(i+1)/(float)(s2p_reso_curv);
703 | float xt,yt;
704 | s2p_cubic_bezier(lx,ly,x0,y0,_x+x1,_y+y1,_x+x2,_y+y2,t,&xt,&yt);
705 | s2p__m_lineto(xt,yt);
706 | }
707 | lx = _x+x2;
708 | ly = _y+y2;
709 | cx = _x+x1;
710 | cy = _y+y1;
711 | }else if (c == 'T' || c == 't'){
712 | float _x = 0, _y = 0;
713 | if (c == 't'){
714 | _x = lx;
715 | _y = ly;
716 | }
717 | if (!(lc == 'Q' || lc == 'q' || lc == 'T' || lc == 't')){
718 | qx = lx;
719 | qy = ly;
720 | }
721 | x0 = (lx-qx)+lx;
722 | y0 = (ly-qy)+ly;
723 | fscanf(fd, "%f", &x1);
724 | s2p__d_next(fd);
725 | fscanf(fd, "%f", &y1);
726 | for (int i = 0; i < s2p_reso_curv; i++){
727 | float t = (float)(i+1)/(float)(s2p_reso_curv);
728 | float xt,yt;
729 | s2p_quadratic_bezier(lx,ly,x0,y0,_x+x1,_y+y1,t,&xt,&yt);
730 | s2p__m_lineto(xt,yt);
731 | }
732 | lx = _x+x1;
733 | ly = _y+y1;
734 | qx = x0;
735 | qy = y0;
736 | }else if (c == 'A' || c == 'a'){
737 | float _x = 0, _y = 0;
738 | if (c == 'a'){
739 | _x = lx;
740 | _y = ly;
741 | }
742 | fscanf(fd, "%f", &x0);//rx
743 | s2p__d_next(fd);
744 | fscanf(fd, "%f", &y0);//ry
745 | s2p__d_next(fd);
746 | fscanf(fd, "%f", &x1);//rot
747 | s2p__d_next(fd);
748 | fscanf(fd, "%d", &f0);//fA
749 | s2p__d_next(fd);
750 | fscanf(fd, "%d", &f1);//fS
751 | s2p__d_next(fd);
752 | fscanf(fd, "%f", &x2);//nx
753 | s2p__d_next(fd);
754 | fscanf(fd, "%f", &y2);//ny
755 |
756 | s2p_arc(lx,ly,_x+x2,_y+y2,f0,f1,x0,y0,x1*M_PI/180.0);
757 | lx = _x+x2;
758 | ly = _y+y2;
759 | }
760 | lc = c;
761 | }
762 | }
763 |
764 | void s2p__parse_path(FILE* fd, s2p_attr_t* attrs, int n_attr){
765 | for (int i = 0; i < n_attr; i++){
766 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
767 | fseek(fd, attrs[i].k, SEEK_SET);
768 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
769 | if (!strcmp(keybuf,"d")){
770 |
771 | s2p__parse_d(fd,attrs[i].v,attrs[i].v_len);
772 | }
773 | }
774 | }
775 |
776 |
777 | void s2p_def_tag(char* name, char tag_type, s2p_tagdef_funptr_t fun){
778 | strncpy(s2p__tagdefs[s2p__ntd].name, name, S2P_MAX_TAG_LEN);
779 | s2p__tagdefs[s2p__ntd].type = tag_type;
780 | s2p__tagdefs[s2p__ntd].func = fun;
781 | s2p__ntd++;
782 | }
783 |
784 | void s2p_parse(FILE* fd){
785 | int c;
786 | int state = S2P__ST_OUT;
787 | long tag;
788 | char tag_len = 0;
789 | char closing = 0;
790 | s2p_attr_t attrs[S2P_MAX_ATTR] = {0};
791 | int n_attr = 0;
792 | char quote = 0;
793 | char cmmttyp = 0;
794 | int endcmmt = 0;
795 | s2p__mati = 0;
796 | S2P__MATSET(s2p__mats[s2p__mati], 1,0,0, 0,1,0);
797 |
798 | long stroke_stack = 0;
799 |
800 | while ((c = fgetc(fd)) != EOF){
801 | // printf("%c %d\n",c,state);
802 | if (state == S2P__ST_OUT){
803 | if (c == '<'){
804 | state = S2P__ST_OPEN;
805 | }
806 | }else if (state == S2P__ST_OPEN){
807 | if (c == '>') goto lbl_close_tag;
808 | if (c == '/') {closing = S2P_TAG_CLOSE; continue;}
809 | if (c == '!'){ state = S2P__ST_EXCL;continue;}
810 | if (c > ' '){
811 | state = S2P__ST_TAG;
812 | tag = ftell(fd)-1;
813 | tag_len++;
814 | }
815 | }else if (state == S2P__ST_TAG){
816 | if (c == '>') goto lbl_close_tag;
817 | if (c == '/') goto lbl_slash;
818 | if (c > ' '){
819 | tag_len++;
820 | }else{
821 | state = S2P__ST_IN;
822 | }
823 | }else if (state == S2P__ST_IN){
824 | if (c == '>'){
825 | goto lbl_close_tag;
826 | }
827 | if (c == '/') goto lbl_slash;
828 | if (c > ' '){
829 | state = S2P__ST_AK;
830 | attrs[n_attr].k = ftell(fd)-1;
831 | attrs[n_attr].k_len++;
832 | }
833 | }else if (state == S2P__ST_AK){
834 | if (c == '>') goto lbl_close_tag;
835 | if (c == '/') goto lbl_slash;
836 | if (c == '='){
837 | state = S2P__ST_AVQ;
838 | }else if (c > ' '){
839 | attrs[n_attr].k_len++;
840 | }else{
841 | state = S2P__ST_AKE;
842 | }
843 | }else if (state == S2P__ST_AKE){
844 | if (c == '>') goto lbl_close_tag;
845 | if (c == '/') goto lbl_slash;
846 | if (c == '='){
847 | state = S2P__ST_AVQ;
848 | }else if (c > ' '){
849 | n_attr ++;
850 | state = S2P__ST_AK;
851 | attrs[n_attr].k = ftell(fd)-1;
852 | attrs[n_attr].k_len++;
853 | }
854 | }else if (state == S2P__ST_AVQ){
855 | if (c == '>') goto lbl_close_tag;
856 | if (c == '/') goto lbl_slash;
857 | if (c == '"' || c == '\''){
858 | quote = c;
859 | state = S2P__ST_AV;
860 | attrs[n_attr].v = ftell(fd);
861 | }
862 | }else if (state == S2P__ST_AV){
863 | if (c == quote){
864 | n_attr ++;
865 | state = S2P__ST_IN;
866 | }else{
867 | attrs[n_attr].v_len++;
868 | }
869 | }else if (state == S2P__ST_EXCL){
870 | cmmttyp = c;
871 | if (c == '[') cmmttyp += 2;
872 | state = S2P__ST_CMMT;
873 | endcmmt = -1;
874 | }else if (state == S2P__ST_CMMT){
875 | if (c == '>' && (endcmmt >= 2 || (cmmttyp != '-' && cmmttyp != ']'))) goto lbl_close_tag;
876 | if (c == cmmttyp) endcmmt ++; else endcmmt = 0;
877 | }
878 | continue;
879 | lbl_slash:;
880 | closing ++;
881 | continue;
882 | lbl_close_tag:;
883 |
884 | if (!tag_len){
885 | goto lbl_reset;
886 | }
887 |
888 | int pos = ftell(fd);
889 |
890 | char tagbuf[S2P_MAX_TAG_LEN+1] = {0};
891 |
892 | fseek(fd, tag, SEEK_SET);
893 | fread(tagbuf,1,S2P__MIN(tag_len,7),fd);
894 |
895 | // fprintf(stderr,"%s %d\n",tagbuf,pos);
896 |
897 | for (int i = 0; i < s2p__ntd; i++){
898 | if (closing != s2p__tagdefs[i].type){
899 | continue;
900 | }
901 | if (!strncmp(s2p__tagdefs[i].name, tagbuf, S2P_MAX_TAG_LEN)){
902 | fseek(fd, pos, SEEK_SET);
903 | s2p__tagdefs[i].func(fd,attrs,n_attr);
904 | goto lbl_done_tag;
905 | }
906 | }
907 |
908 | if (!strcmp(tagbuf,"svg")){
909 | if (closing == 0){
910 | float x=0,y=0,w=300,h=150,vx,vy,vw,vh,par=1;
911 | char set_whv = 0;
912 | for (int i = 0; i < n_attr; i++){
913 | char keybuf[S2P_MAX_AK_LEN+1] = {0};
914 | fseek(fd, attrs[i].k, SEEK_SET);
915 | fread(keybuf,1,S2P__MIN(attrs[i].k_len,S2P_MAX_AK_LEN),fd);
916 | if (!strcmp(keybuf,"x")){
917 | fseek(fd, attrs[i].v, SEEK_SET);
918 | fscanf(fd, "%f", &x);
919 | }else if (!strcmp(keybuf,"y")){
920 | fseek(fd, attrs[i].v, SEEK_SET);
921 | fscanf(fd, "%f", &y);
922 | }else if (!strcmp(keybuf,"width")){
923 | set_whv |= 4;
924 | fseek(fd, attrs[i].v, SEEK_SET);
925 | fscanf(fd, "%f", &w);
926 | }else if (!strcmp(keybuf,"height")){
927 | set_whv |= 2;
928 | fseek(fd, attrs[i].v, SEEK_SET);
929 | fscanf(fd, "%f", &h);
930 |
931 | }else if (!strcmp(keybuf,"viewBox")){
932 | set_whv |= 1;
933 | fseek(fd, attrs[i].v, SEEK_SET);
934 | fscanf(fd, "%f", &vx);
935 | s2p__d_next(fd);
936 | fscanf(fd, "%f", &vy);
937 | s2p__d_next(fd);
938 | fscanf(fd, "%f", &vw);
939 | s2p__d_next(fd);
940 | fscanf(fd, "%f", &vh);
941 | }else if (!strncmp(keybuf,"prese",5)){
942 | fseek(fd, attrs[i].v, SEEK_SET);
943 | s2p__d_next(fd);
944 | if (fgetc(fd) == 'n'){
945 | par = 0;
946 | }
947 | }
948 | }
949 |
950 |
951 | if (set_whv == 6 || set_whv == 0){
952 | vx = 0; vy = 0; vw = w; vh = h;
953 | }else if (set_whv == 1){
954 | w = vw; h = vh;
955 | }else if (set_whv == 5){
956 | h = w * vh / vw;
957 | }else if (set_whv == 3){
958 | w = h * vw / vh;
959 | }else if (set_whv == 2){
960 | w = h;
961 | vx = 0; vy = 0; vw = w; vh = h;
962 | }else if (set_whv == 4){
963 | h = w;
964 | vx = 0; vy = 0; vw = w; vh = h;
965 | }
966 |
967 | if (!s2p__mati){
968 | S2P_SETDIM(w,h);
969 | }
970 |
971 | s2p__mati ++;
972 | memcpy(s2p__mats+s2p__mati,s2p__mats+(s2p__mati-1),9*sizeof(float));
973 |
974 | float sx = w/vw;
975 | float sy = h/vh;
976 | float m[9] = {sx,0,-vx*sx+x, 0,sy,-vy*sy+y, 0,0,1};
977 | if (par){
978 | float s = fmin(sx,sy);
979 | float px = (w-vw*s)/2.0;
980 | float py = (h-vh*s)/2.0;
981 | m[0] = s; m[2] = -vx*s+x+px;
982 | m[4] = s; m[5] = -vy*s+y+py;
983 | }
984 |
985 | s2p__matmul(s2p__mats[s2p__mati],m,s2p__mats[s2p__mati]);
986 | s2p__get_transf(fd,attrs,n_attr,s2p__mats[s2p__mati]);
987 |
988 | }else if (closing == S2P_TAG_CLOSE){
989 | s2p__mati--;
990 | }
991 |
992 | }else if (!strcmp(tagbuf,"g")){
993 |
994 | if (closing == S2P_TAG_OPEN){
995 | s2p__mati ++;
996 | memcpy(s2p__mats+s2p__mati,s2p__mats+(s2p__mati-1),9*sizeof(float));
997 | s2p__get_transf(fd,attrs,n_attr,s2p__mats[s2p__mati]);
998 |
999 | if (s2p_skip_nostroke){
1000 | stroke_stack <<= 1;
1001 | stroke_stack |= s2p__get_stroke(fd,attrs,n_attr);
1002 | }
1003 |
1004 | }else if (closing == S2P_TAG_CLOSE){
1005 | s2p__mati--;
1006 | stroke_stack >>= 1;
1007 | }
1008 | }else if (closing != S2P_TAG_CLOSE){
1009 |
1010 | if (s2p_skip_nostroke){
1011 | if (!stroke_stack && !s2p__get_stroke(fd,attrs,n_attr)){
1012 | goto lbl_done_tag;
1013 | }
1014 | }
1015 |
1016 | s2p__mati ++;
1017 | memcpy(s2p__mats+s2p__mati,s2p__mats+(s2p__mati-1),9*sizeof(float));
1018 | s2p__get_transf(fd,attrs,n_attr,s2p__mats[s2p__mati]);
1019 |
1020 | if (!strcmp(tagbuf,"line")){
1021 |
1022 | s2p__parse_line(fd,attrs,n_attr);
1023 |
1024 | }else if (!strncmp(tagbuf,"poly",4)){
1025 |
1026 | s2p__parse_poly(fd,attrs,n_attr,tagbuf[4]=='g');
1027 |
1028 | }else if (!strcmp(tagbuf,"path")){
1029 |
1030 | s2p__parse_path(fd,attrs,n_attr);
1031 |
1032 | }else if (!strcmp(tagbuf,"rect")){
1033 |
1034 | s2p__parse_rect(fd,attrs,n_attr);
1035 |
1036 | }else if (!strcmp(tagbuf,"ellipse") || !strcmp(tagbuf,"circle")){
1037 |
1038 | s2p__parse_ellipse(fd,attrs,n_attr);
1039 |
1040 | }
1041 | s2p__mati --;
1042 |
1043 | }
1044 | lbl_done_tag:;
1045 |
1046 | fseek(fd,pos,SEEK_SET);
1047 |
1048 | lbl_reset:;
1049 | tag_len = 0;
1050 | tag = 0;
1051 | n_attr = 0;
1052 | memset(attrs,0,sizeof(attrs));
1053 | state = S2P__ST_OUT;
1054 | closing = 0;
1055 | }
1056 | }
1057 |
1058 | void s2p_parse_from_file(char* path){
1059 | FILE* fd = fopen(path,"r");
1060 | s2p_parse(fd);
1061 | fclose(fd);
1062 | }
1063 |
1064 | void s2p_parse_from_str(char* s, int n){
1065 | if (n == -1) n = strlen(s);
1066 | FILE* fd = fmemopen(s,n,"r");
1067 | s2p_parse(fd);
1068 | fclose(fd);
1069 | }
1070 |
1071 | #endif
1072 |
--------------------------------------------------------------------------------
/examples/hershey_text.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #define S2P_SETDIM(w,h) {printf("");
660 |
661 | }
--------------------------------------------------------------------------------