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

Revisions

View the latest version of libbirdfont/Svg.vala.
Fix translation
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 public 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.get_visible_paths ()) { 27 if (p.stroke == 0) { 28 write_path_as_glyph (p, svg, g); 29 } else { 30 stroke_list = p.get_completed_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 if (!p.is_open ()) { 84 m = p.points.get (0); 85 add_abs_next ((!) n, m, svg, g, do_glyph); 86 close_path (svg); 87 } 88 } 89 90 private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) { 91 if (start.right_handle.type == PointType.LINE_QUADRATIC) { 92 add_abs_line_to (start, end, svg, g, do_glyph); 93 } else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) { 94 add_abs_line_to (start, end, svg, g, do_glyph); 95 } else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) { 96 add_quadratic_abs_path (start, end, svg, g, do_glyph); 97 } else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) { 98 add_double_quadratic_abs_path (start, end, svg, g, do_glyph); 99 } else { 100 add_cubic_abs_path (start, end, svg, g, do_glyph); 101 } 102 } 103 104 private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) { 105 double left = g.left_limit; 106 double baseline = -BirdFont.get_current_font ().base_line; 107 Font font = BirdFont.get_current_font (); 108 double height = font.top_limit - font.base_line; 109 110 svg.append_printf ("M"); 111 112 if (!to_glyph) { 113 svg.append_printf ("%s ", round (ep.x - left)); 114 svg.append_printf ("%s ", round (-ep.y + height)); 115 } else { 116 svg.append_printf ("%s ", round (ep.x - left)); 117 svg.append_printf ("%s ", round (ep.y + baseline)); 118 } 119 } 120 121 private static void close_path (StringBuilder svg) { 122 svg.append ("z"); 123 } 124 125 private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) { 126 double baseline = -BirdFont.get_current_font ().base_line; 127 double left = g.left_limit; 128 Font font = BirdFont.get_current_font (); 129 double height = font.top_limit - font.base_line; 130 131 132 double xa, ya, xb, yb; 133 134 Path.get_line_points (start, stop, out xa, out ya, out xb, out yb); 135 136 double center_x = Glyph.xc (); 137 double center_y = Glyph.yc (); 138 139 svg.append ("L"); 140 141 if (!to_glyph) { 142 svg.append_printf ("%s ", round (xb - center_x - left)); 143 svg.append_printf ("%s ", round (yb - center_y + height)); 144 } else { 145 svg.append_printf ("%s ", round (xb - center_x - left)); 146 svg.append_printf ("%s ", round (-yb + center_y + baseline)); 147 } 148 } 149 150 private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { 151 EditPoint middle; 152 double x, y; 153 154 x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2; 155 y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2; 156 157 middle = new EditPoint (x, y, PointType.QUADRATIC); 158 middle.right_handle = end.get_left_handle ().copy (); 159 160 add_quadratic_abs_path (start, middle, svg, g, to_glyph); 161 add_quadratic_abs_path (middle, end, svg, g, to_glyph); 162 } 163 164 private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { 165 double left = g.left_limit; 166 double baseline = -BirdFont.get_current_font ().base_line; 167 Font font = BirdFont.get_current_font (); 168 double height = font.top_limit - font.base_line; 169 170 double xa, ya, xb, yb, xc, yc, xd, yd; 171 172 Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); 173 174 double center_x = Glyph.xc (); 175 double center_y = Glyph.yc (); 176 177 // cubic path 178 if (!to_glyph) { 179 svg.append_printf ("Q"); 180 181 svg.append_printf ("%s ", round (xb - center_x - left)); 182 svg.append_printf ("%s ", round (yb - center_y + height)); 183 184 svg.append_printf ("%s ", round (xd - center_x - left)); 185 svg.append_printf ("%s ", round (yd - center_y + height)); 186 187 } else { 188 svg.append_printf ("Q"); 189 190 svg.append_printf ("%s ", round (xb - center_x - left)); 191 svg.append_printf ("%s ", round (-yb + center_y + baseline)); 192 193 svg.append_printf ("%s ", round (xd - center_x - left)); 194 svg.append_printf ("%s ", round (-yd + center_y + baseline)); 195 } 196 } 197 198 private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { 199 double left = g.left_limit; 200 double baseline = -BirdFont.get_current_font ().base_line; 201 Font font = BirdFont.get_current_font (); 202 double height = font.top_limit - font.base_line; 203 204 double xa, ya, xb, yb, xc, yc, xd, yd; 205 206 Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); 207 208 double center_x = Glyph.xc (); 209 double center_y = Glyph.yc (); 210 211 // cubic path 212 if (!to_glyph) { 213 svg.append_printf ("C"); 214 215 svg.append_printf ("%s ", round (xb - center_x - left)); 216 svg.append_printf ("%s ", round (yb - center_y + height)); 217 218 svg.append_printf ("%s ", round (xc - center_x - left)); 219 svg.append_printf ("%s ", round (yc - center_y + height)); 220 221 svg.append_printf ("%s ", round (xd - center_x - left)); 222 svg.append_printf ("%s ", round (yd - center_y + height)); 223 224 } else { 225 svg.append_printf ("C"); 226 227 svg.append_printf ("%s ", round (xb - center_x - left)); 228 svg.append_printf ("%s ", round (-yb + center_y + baseline)); 229 230 svg.append_printf ("%s ", round (xc - center_x - left)); 231 svg.append_printf ("%s ", round (-yc + center_y + baseline)); 232 233 svg.append_printf ("%s ", round (xd - center_x - left)); 234 svg.append_printf ("%s ", round (-yd + center_y + baseline)); 235 } 236 } 237 238 /** Draw path from svg font data. */ 239 public static void draw_svg_path (Context cr, string svg, double x, double y) { 240 double x1, x2, x3; 241 double y1, y2, y3; 242 double px, py; 243 string[] d = svg.split (" "); 244 245 if (d.length == 0) { 246 return; 247 } 248 249 px = 0; 250 py = 0; 251 252 cr.save (); 253 254 cr.set_line_width (0); 255 256 if (svg == "") { 257 return; 258 } 259 260 for (int i = 0; i < d.length; i++) { 261 262 // trim off leading white space 263 while (d[i].index_of (" ") == 0) { 264 d[i] = d[i].substring (1); // FIXME: maybe no ascii 265 } 266 267 if (d[i].index_of ("L") == 0) { 268 x1 = double.parse (d[i].substring (1)) + x; 269 y1 = -double.parse (d[i+1]) + y; 270 cr.line_to (x1, y1); 271 272 px = x1; 273 py = y1; 274 continue; 275 } 276 277 if (d[i].index_of ("Q") == 0) { 278 x1 = double.parse (d[i].substring (1)) + x; 279 y1 = -double.parse (d[i+1]) + y; 280 281 x2 = double.parse (d[i+2]) + x; 282 y2 = -double.parse (d[i+3]) + y; 283 284 cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); 285 286 px = x2; 287 py = y2; 288 continue; 289 } 290 291 if (d[i].index_of ("C") == 0) { 292 x1 = double.parse (d[i].substring (1)) + x; 293 y1 = -double.parse (d[i+1]) + y; 294 295 x2 = double.parse (d[i+2]) + x; 296 y2 = -double.parse (d[i+3]) + y; 297 298 x3 = double.parse (d[i+4]) + x; 299 y3 = -double.parse (d[i+5]) + y; 300 301 cr.curve_to (x1, y1, x2, y2, x3, y3); 302 303 px = x3; 304 py = y3; 305 continue; 306 } 307 308 if (d[i].index_of ("M") == 0) { 309 x1 = double.parse (d[i].substring (1)) + x; 310 y1 = -double.parse (d[i+1]) + y; 311 312 cr.move_to (x1, y1); 313 314 px = x1; 315 py = y1; 316 continue; 317 } 318 319 if (d[i].index_of ("zM") == 0) { 320 cr.close_path (); 321 322 x1 = double.parse (d[i].substring (2)) + x; 323 y1 = -double.parse (d[i+1]) + y; 324 325 cr.move_to (x1, y1); 326 327 px = x1; 328 py = y1; 329 continue; 330 } 331 332 if (d[i].index_of ("z") == 0) { 333 cr.close_path (); 334 continue; 335 } 336 337 } 338 339 cr.fill (); 340 cr.restore (); 341 } 342 343 } 344 345 internal static string round (double p) { 346 string v = p.to_string (); 347 char[] c = new char [501]; 348 349 v = p.format (c, "%3.15f"); 350 351 if (v.index_of ("e") != -1) { 352 return "0.0"; 353 } 354 355 return v; 356 } 357 358 } 359