The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Svg.vala in /libbirdfont

This file is a part of the Birdfont project.

Contributing

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