.
1 /*
2 Copyright (C) 2012 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16
17 namespace BirdFont {
18
19 class Svg {
20
21 /** Export to svg glyph data. */
22 public static string to_svg_glyph (Glyph g) {
23 StringBuilder svg = new StringBuilder ();
24 PathList stroke_list;
25
26 foreach (Path p in g.path_list) {
27 if (p.stroke == 0) {
28 write_path_as_glyph (p, svg, g);
29 } else {
30 stroke_list = p.get_stroke ();
31 write_paths_as_glyph (stroke_list, svg, g);
32 }
33 }
34
35 return svg.str;
36 }
37
38 /** Export to svg-font data. */
39 public static string to_svg_path (Path pl, Glyph g) {
40 StringBuilder svg = new StringBuilder ();
41 pl.create_list ();
42 write_path (pl, svg, g, false);
43 return svg.str;
44 }
45
46 private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) {
47 foreach (Path p in pl.paths) {
48 write_path_as_glyph (p, svg, g);
49 }
50 }
51
52 private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) {
53 write_path (pl, svg, g, true);
54 }
55
56 private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) {
57 int i = 0;
58 EditPoint? n = null;
59 EditPoint m;
60
61 if (p.points.size < 2) {
62 return;
63 }
64
65 p.create_list ();
66
67 foreach (var e in p.points) {
68 if (i == 0) {
69 add_abs_start (e, svg, g, do_glyph);
70 i++;
71 n = e;
72 continue;
73 }
74
75 m = (!) n;
76
77 add_abs_next (m, e, svg, g, do_glyph);
78
79 n = e;
80 i++;
81 }
82
83 m = p.points.get (0);
84 add_abs_next ((!) n, m, svg, g, do_glyph);
85
86 close_path (svg);
87 }
88
89 private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) {
90 if (start.right_handle.type == PointType.LINE_QUADRATIC) {
91 add_abs_line_to (start, end, svg, g, do_glyph);
92 } else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) {
93 add_abs_line_to (start, end, svg, g, do_glyph);
94 } else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) {
95 add_quadratic_abs_path (start, end, svg, g, do_glyph);
96 } else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) {
97 add_double_quadratic_abs_path (start, end, svg, g, do_glyph);
98 } else {
99 add_cubic_abs_path (start, end, svg, g, do_glyph);
100 }
101 }
102
103 private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) {
104 double left = g.left_limit;
105 double baseline = -BirdFont.get_current_font ().base_line;
106 double height = g.get_height ();
107
108 svg.append_printf ("M");
109
110 if (!to_glyph) {
111 svg.append_printf ("%s ", round (ep.x - left));
112 svg.append_printf ("%s ", round (-ep.y + height / 2));
113 } else {
114 svg.append_printf ("%s ", round (ep.x - left));
115 svg.append_printf ("%s ", round (ep.y + baseline));
116 }
117 }
118
119 private static void close_path (StringBuilder svg) {
120 svg.append ("z");
121 }
122
123 private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) {
124 double baseline = -BirdFont.get_current_font ().base_line;
125 double left = g.left_limit;
126 double height = g.get_height ();
127
128 double xa, ya, xb, yb;
129
130 Path.get_line_points (start, stop, out xa, out ya, out xb, out yb);
131
132 double center_x = Glyph.xc ();
133 double center_y = Glyph.yc ();
134
135 svg.append ("L");
136
137 if (!to_glyph) {
138 svg.append_printf ("%s ", round (xb - center_x - left));
139 svg.append_printf ("%s ", round (yb - center_y + height / 2));
140 } else {
141 svg.append_printf ("%s ", round (xb - center_x - left));
142 svg.append_printf ("%s ", round (-yb + center_y + baseline));
143 }
144 }
145
146 private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) {
147 EditPoint middle;
148 double x, y;
149
150 x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2;
151 y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2;
152
153 middle = new EditPoint (x, y, PointType.QUADRATIC);
154 middle.right_handle = end.get_left_handle ().copy ();
155
156 add_quadratic_abs_path (start, middle, svg, g, to_glyph);
157 add_quadratic_abs_path (middle, end, svg, g, to_glyph);
158 }
159
160 private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) {
161 double left = g.left_limit;
162 double baseline = -BirdFont.get_current_font ().base_line;
163 double height = g.get_height ();
164
165 double xa, ya, xb, yb, xc, yc, xd, yd;
166
167 Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);
168
169 double center_x = Glyph.xc ();
170 double center_y = Glyph.yc ();
171
172 // cubic path
173 if (!to_glyph) {
174 svg.append_printf ("Q");
175
176 svg.append_printf ("%s ", round (xb - center_x - left));
177 svg.append_printf ("%s ", round (yb - center_y + height / 2));
178
179 svg.append_printf ("%s ", round (xd - center_x - left));
180 svg.append_printf ("%s ", round (yd - center_y + height / 2));
181
182 } else {
183 svg.append_printf ("Q");
184
185 svg.append_printf ("%s ", round (xb - center_x - left));
186 svg.append_printf ("%s ", round (-yb + center_y + baseline));
187
188 svg.append_printf ("%s ", round (xd - center_x - left));
189 svg.append_printf ("%s ", round (-yd + center_y + baseline));
190 }
191 }
192
193 private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) {
194 double left = g.left_limit;
195 double baseline = -BirdFont.get_current_font ().base_line;
196 double height = g.get_height ();
197
198 double xa, ya, xb, yb, xc, yc, xd, yd;
199
200 Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);
201
202 double center_x = Glyph.xc ();
203 double center_y = Glyph.yc ();
204
205 // cubic path
206 if (!to_glyph) {
207 svg.append_printf ("C");
208
209 svg.append_printf ("%s ", round (xb - center_x - left));
210 svg.append_printf ("%s ", round (yb - center_y + height / 2));
211
212 svg.append_printf ("%s ", round (xc - center_x - left));
213 svg.append_printf ("%s ", round (yc - center_y + height / 2));
214
215 svg.append_printf ("%s ", round (xd - center_x - left));
216 svg.append_printf ("%s ", round (yd - center_y + height / 2));
217
218 } else {
219 svg.append_printf ("C");
220
221 svg.append_printf ("%s ", round (xb - center_x - left));
222 svg.append_printf ("%s ", round (-yb + center_y + baseline));
223
224 svg.append_printf ("%s ", round (xc - center_x - left));
225 svg.append_printf ("%s ", round (-yc + center_y + baseline));
226
227 svg.append_printf ("%s ", round (xd - center_x - left));
228 svg.append_printf ("%s ", round (-yd + center_y + baseline));
229 }
230 }
231
232 /** Draw path from svg font data. */
233 public static void draw_svg_path (Context cr, string svg, double x, double y) {
234 double x1, x2, x3;
235 double y1, y2, y3;
236 double px, py;
237 string[] d = svg.split (" ");
238
239 if (d.length == 0) {
240 return;
241 }
242
243 px = 0;
244 py = 0;
245
246 cr.save ();
247
248 cr.set_line_width (0);
249
250 if (svg == "") {
251 return;
252 }
253
254 for (int i = 0; i < d.length; i++) {
255
256 // trim off leading white space
257 while (d[i].index_of (" ") == 0) {
258 d[i] = d[i].substring (1); // FIXME: maybe no ascii
259 }
260
261 if (d[i].index_of ("L") == 0) {
262 x1 = double.parse (d[i].substring (1)) + x;
263 y1 = -double.parse (d[i+1]) + y;
264 cr.line_to (x1, y1);
265
266 px = x1;
267 py = y1;
268 continue;
269 }
270
271 if (d[i].index_of ("Q") == 0) {
272 x1 = double.parse (d[i].substring (1)) + x;
273 y1 = -double.parse (d[i+1]) + y;
274
275 x2 = double.parse (d[i+2]) + x;
276 y2 = -double.parse (d[i+3]) + y;
277
278 cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2);
279
280 px = x2;
281 py = y2;
282 continue;
283 }
284
285 if (d[i].index_of ("C") == 0) {
286 x1 = double.parse (d[i].substring (1)) + x;
287 y1 = -double.parse (d[i+1]) + y;
288
289 x2 = double.parse (d[i+2]) + x;
290 y2 = -double.parse (d[i+3]) + y;
291
292 x3 = double.parse (d[i+4]) + x;
293 y3 = -double.parse (d[i+5]) + y;
294
295 cr.curve_to (x1, y1, x2, y2, x3, y3);
296
297 px = x3;
298 py = y3;
299 continue;
300 }
301
302 if (d[i].index_of ("M") == 0) {
303 x1 = double.parse (d[i].substring (1)) + x;
304 y1 = -double.parse (d[i+1]) + y;
305
306 cr.move_to (x1, y1);
307
308 px = x1;
309 py = y1;
310 continue;
311 }
312
313 if (d[i].index_of ("zM") == 0) {
314 cr.close_path ();
315
316 x1 = double.parse (d[i].substring (2)) + x;
317 y1 = -double.parse (d[i+1]) + y;
318
319 cr.move_to (x1, y1);
320
321 px = x1;
322 py = y1;
323 continue;
324 }
325
326 if (d[i].index_of ("z") == 0) {
327 cr.close_path ();
328 continue;
329 }
330
331 }
332
333 cr.fill ();
334 cr.restore ();
335 }
336
337 }
338
339 internal static string round (double p) {
340 string v = p.to_string ();
341 char[] c = new char [501];
342
343 v = p.format (c, "%3.15f");
344
345 if (v.index_of ("e") != -1) {
346 return "0.0";
347 }
348
349 return v;
350 }
351
352 }
353