The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

GlyfData.vala in libbirdfont/OpenFontFormat

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/OpenFontFormat/GlyfData.vala.
Add extrema only if necessary
1 /* 2 Copyright (C) 2012, 2013, 2014 2017 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 Math; 16 17 namespace BirdFont { 18 19 public class CoordinateFlags : GLib.Object { 20 /** TTF coordinate flags. */ 21 22 public const uint8 NONE = 0; 23 public const uint8 ON_PATH = 1 << 0; 24 public const uint8 X_SHORT_VECTOR = 1 << 1; 25 public const uint8 Y_SHORT_VECTOR = 1 << 2; 26 public const uint8 REPEAT = 1 << 3; 27 28 // same flag or short vector sign flag 29 public const uint8 X_IS_SAME = 1 << 4; 30 public const uint8 Y_IS_SAME = 1 << 5; 31 public const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4; 32 public const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5; 33 34 } 35 36 /** Data for one entry in the glyf table. */ 37 public class GlyfData : GLib.Object { 38 public Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); 39 public Gee.ArrayList<EditPoint> points = new Gee.ArrayList<EditPoint> (); 40 public Gee.ArrayList<uint16> end_points = new Gee.ArrayList<uint16> (); 41 public Gee.ArrayList<uint8> flags = new Gee.ArrayList<uint8> (); 42 public Gee.ArrayList<int16> coordinate_x = new Gee.ArrayList<int16> (); 43 public Gee.ArrayList<int16> coordinate_y = new Gee.ArrayList<int16> (); 44 45 uint16 end_point = 0; 46 uint16 nflags = 0; 47 48 Glyph glyph; 49 50 public int16 bounding_box_xmin = 0; 51 public int16 bounding_box_ymin = 0; 52 public int16 bounding_box_xmax = 0; 53 public int16 bounding_box_ymax = 0; 54 55 private static double UNITS { 56 get { return HeadTable.UNITS; } 57 } 58 59 public GlyfData (Glyph g) { 60 PathList all_quadratic = g.get_quadratic_paths (); 61 PathList qp = new PathList (); 62 63 glyph = g; 64 65 int i = 0; 66 foreach (Path p in all_quadratic.paths) { 67 if (p.points.size < 2) { 68 warning (@"Missing points, $(points.size) points in path."); 69 continue; 70 } 71 72 if (likely (!is_empty (p))) { 73 qp.add (p); 74 } else { 75 warning (@"Path number $i is empty in $(glyph.get_name ())"); 76 } 77 78 i++; 79 } 80 81 // Add points at extrema 82 add_extrema_to_path (qp); 83 84 points.clear (); 85 paths.clear (); 86 foreach (Path p in qp.paths) { 87 paths.add (p); 88 89 foreach (EditPoint ep in p.points) { 90 points.add (ep); 91 } 92 } 93 94 if (paths.size > 0) { 95 process_end_points (); 96 process_flags (); 97 process_x (); 98 process_y (); 99 process_bounding_box (); 100 } 101 } 102 103 public static void add_extrema_to_path (PathList path_list) { 104 double x0, y0, x1, y1, x2, y2, x3, y3; 105 double minx, maxx, miny, maxy; 106 Path path_minx, path_maxx, path_miny, path_maxy; 107 108 path_minx = new Path (); 109 path_maxx = new Path (); 110 path_miny = new Path (); 111 path_maxy = new Path (); 112 113 minx = Glyph.CANVAS_MAX; 114 miny = Glyph.CANVAS_MAX; 115 maxx = Glyph.CANVAS_MIN; 116 maxy = Glyph.CANVAS_MIN; 117 118 x0 = 0; 119 y0 = 0; 120 x1 = 0; 121 y1 = 0; 122 x2 = 0; 123 y2 = 0; 124 x3 = 0; 125 y3 = 0; 126 127 foreach (Path next_path in path_list.paths) { 128 if (next_path.points.size < 2) { 129 warning (@"Missing points, $(next_path.points.size) points in path."); 130 continue; 131 } 132 133 next_path.all_of_path ((x, y) => { 134 if (x < minx) { 135 x0 = x; 136 y0 = y; 137 minx = x; 138 path_minx = next_path; 139 } 140 141 if (x > maxx) { 142 x1 = x; 143 y1 = y; 144 maxx = x; 145 path_maxx = next_path; 146 } 147 148 if (y < miny) { 149 x2 = x; 150 y2 = y; 151 miny = y; 152 path_miny = next_path; 153 } 154 155 if (y > maxy) { 156 x3 = x; 157 y3 = y; 158 maxy = y; 159 path_maxy = next_path; 160 } 161 162 return true; 163 }, 3000); 164 } 165 166 if (!has_extrema (path_minx, x0 + 0.001, true, true)) { 167 path_minx.insert_new_point_on_path_at (x0 - 0.001, y0); 168 } 169 170 if (!has_extrema (path_maxx, x1 - 0.001, true, false)) { 171 path_maxx.insert_new_point_on_path_at (x1 + 0.001, y1); 172 } 173 174 if (!has_extrema (path_maxy, y2 + 0.001, false, true)) { 175 path_maxy.insert_new_point_on_path_at (x2, y2 - 0.001); 176 } 177 178 if (!has_extrema (path_miny, y3 - 0.001, false, false)) { 179 path_miny.insert_new_point_on_path_at (x3, y3 + 0.001); 180 } 181 } 182 183 public static bool has_extrema (Path path, double coordinate, bool x, bool min) { 184 bool has_extrema = false; 185 186 if (x && min) { 187 path.all_segments ((start, stop) => { 188 if (start.x < coordinate) { 189 has_extrema = true; 190 return false; 191 } 192 193 return true; 194 }); 195 } 196 197 if (x && !min) { 198 path.all_segments ((start, stop) => { 199 if (start.x > coordinate) { 200 has_extrema = true; 201 return false; 202 } 203 204 return true; 205 }); 206 } 207 208 if (!x && min) { 209 path.all_segments ((start, stop) => { 210 if (start.y < coordinate) { 211 has_extrema = true; 212 return false; 213 } 214 215 return true; 216 }); 217 } 218 219 if (!x && !min) { 220 path.all_segments ((start, stop) => { 221 if (start.y > coordinate) { 222 has_extrema = true; 223 return false; 224 } 225 226 return true; 227 }); 228 } 229 230 return has_extrema; 231 } 232 233 bool is_empty (Path p) { 234 EditPoint? last = null; 235 236 if (unlikely (p.points.size < 2)) { 237 return true; 238 } 239 240 foreach (EditPoint ep in p.points) { 241 if (last != null && !ep.equals ((!) last)) { 242 return false; 243 } 244 last = ep; 245 } 246 247 return true; 248 } 249 250 public uint16 get_end_point () { 251 return end_point; 252 } 253 254 public int16 get_ncontours () { 255 return (int16) paths.size; 256 } 257 258 public uint16 get_nflags () { 259 return nflags; 260 } 261 262 /** Count off curve points and on curve points. 263 * @return the number of points or uint16.MAX if more than uint16.MAX points where found. 264 */ 265 public int get_num_points () { 266 int points = 0; 267 268 foreach (Path quadratic in paths) { 269 points += 2 * quadratic.points.size; 270 271 if (points >= uint16.MAX) { 272 return uint16.MAX; 273 } 274 } 275 276 return points; 277 } 278 279 void process_end_points () { 280 uint16 last_end_point = 0; 281 PointType type; 282 283 end_points.clear (); 284 end_point = 0; 285 286 foreach (Path quadratic in paths) { 287 if (unlikely (quadratic.points.size == 0)) { 288 warning (@"No points in path (before conversion $(quadratic.points.size))"); 289 continue; 290 } 291 292 if (unlikely (quadratic.points.size < 2)) { 293 warning ("A path contains less than three points, it will not be exported. Path number: $path_number"); 294 continue; 295 } 296 297 foreach (EditPoint e in quadratic.points) { 298 if (unlikely (nflags == uint16.MAX - 1)) { 299 warning (@"Too many end points in $(glyph.get_name ())"); 300 break; 301 } 302 303 end_point++; 304 type = e.get_right_handle ().type; 305 306 // off curve 307 if (unlikely (nflags == uint16.MAX - 1)) { 308 warning (@"Too many end points in $(glyph.get_name ())"); 309 break; 310 } 311 312 end_point++; 313 } 314 end_points.add (end_point - 1); 315 316 if (unlikely (end_point - 1 < last_end_point)) { 317 warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point) ($(end_point - 1) < $last_end_point)"); 318 } 319 320 last_end_point = end_point - 1; 321 } 322 323 if (unlikely (end_point == 0)) { 324 warning (@"End point is zero for glyph $(glyph.get_name ())"); 325 } 326 } 327 328 void process_flags () { 329 PointType type; 330 331 flags = new Gee.ArrayList<uint8> (); 332 nflags = 0; 333 334 foreach (Path p in paths) { 335 foreach (EditPoint e in p.points) { 336 flags.add (CoordinateFlags.ON_PATH); 337 nflags++; 338 339 type = e.get_right_handle ().type; 340 341 // off curve 342 flags.add (CoordinateFlags.NONE); 343 344 if (unlikely (nflags == uint16.MAX)) { 345 warning (@"Too many flags in $(glyph.get_name ())"); 346 return; 347 } 348 349 nflags++; 350 } 351 } 352 } 353 354 public static double tie_to_ttf_grid_x (Glyph glyph, double x) { 355 double ttf_x; 356 ttf_x = rint (x * UNITS - glyph.left_limit * UNITS); 357 return (ttf_x / UNITS) + glyph.left_limit; 358 } 359 360 public static double tie_to_ttf_grid_y (Font font, double y) { 361 double ttf_y; 362 ttf_y = rint (y * UNITS - font.base_line * UNITS); 363 return (ttf_y / UNITS) + font.base_line; 364 } 365 366 void process_x () { 367 double prev = 0; 368 double x; 369 PointType type; 370 371 coordinate_x.clear (); 372 foreach (Path p in paths) { 373 foreach (EditPoint e in p.points) { 374 x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS); 375 coordinate_x.add ((int16) x); 376 377 prev = rint (e.x * UNITS - glyph.left_limit * UNITS); 378 379 type = e.get_right_handle ().type; 380 381 // off curve 382 x = rint (e.get_right_handle ().x * UNITS - prev - glyph.left_limit * UNITS); 383 coordinate_x.add ((int16) x); 384 385 prev = rint (e.get_right_handle ().x * UNITS - glyph.left_limit * UNITS); 386 } 387 } 388 } 389 390 void process_y () { 391 double prev = 0; 392 double y; 393 Font font = OpenFontFormatWriter.get_current_font (); 394 PointType type; 395 int epi = 0; 396 397 coordinate_y.clear (); 398 399 int path_number = 0; 400 401 foreach (Path p in paths) { 402 foreach (EditPoint e in p.points) { 403 y = rint (e.y * UNITS - prev - font.base_line * UNITS); 404 coordinate_y.add ((int16) y); 405 406 if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { 407 warning (@"Point on point in TTF. Index $(coordinate_y.size - 1) " 408 + @"Path: $path_number in $(glyph.get_name ())"); 409 } 410 411 prev = rint (e.y * UNITS - font.base_line * UNITS); 412 413 type = e.get_right_handle ().type; 414 415 // off curve 416 y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS); 417 coordinate_y.add ((int16) y); 418 419 prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS); 420 epi++; 421 } 422 423 path_number++; 424 } 425 } 426 427 void process_bounding_box () { 428 int16 last = 0; 429 int i = 0; 430 431 bounding_box_xmin = int16.MAX; 432 bounding_box_ymin = int16.MAX; 433 bounding_box_xmax = int16.MIN; 434 bounding_box_ymax = int16.MIN; 435 436 if (coordinate_x.size == 0) { 437 warning ("no points in coordinate_y"); 438 } 439 440 foreach (int16 c in coordinate_x) { 441 c += last; 442 443 if (c < bounding_box_xmin) { 444 bounding_box_xmin = c; 445 } 446 447 if (c > bounding_box_xmax) { 448 bounding_box_xmax = c; 449 } 450 451 last = c; 452 i++; 453 } 454 455 if (coordinate_y.size == 0) { 456 warning ("no points in coordinate_y"); 457 } 458 459 last = 0; 460 i = 0; 461 foreach (int16 c in coordinate_y) { 462 c += last; 463 464 if (c < bounding_box_ymin) { 465 bounding_box_ymin = c; 466 } 467 468 if (c > bounding_box_ymax) { 469 bounding_box_ymax = c; 470 } 471 472 last = c; 473 i++; 474 } 475 476 printd (@"Bounding box: $bounding_box_xmin,$bounding_box_ymin $bounding_box_xmax,$bounding_box_ymax\n"); 477 } 478 } 479 480 } 481 482