The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

GlyfTable.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/GlyfTable.vala.
Use other types of graphical objects
1 /* 2 Copyright (C) 2012 2013 2014 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 namespace BirdFont { 16 17 public class GlyfTable : OtfTable { 18 // Flags for composite glyph 19 static const uint16 BOTH_ARE_WORDS = 1 << 0; 20 static const uint16 BOTH_ARE_XY_VALUES = 1 << 1; 21 static const uint16 ROUND_TO_GRID = 1 << 2; 22 static const uint16 SCALE = 1 << 3; 23 static const uint16 RESERVED = 1 << 4; 24 static const uint16 MORE_COMPONENTS = 1 << 5; 25 static const uint16 SCALE_X_Y = 1 << 6; 26 static const uint16 SCALE_WITH_ROTATTION = 1 << 7; 27 static const uint16 INSTRUCTIONS = 1 << 8; 28 29 public int16 xmin = int16.MAX; 30 public int16 ymin = int16.MAX; 31 public int16 xmax = int16.MIN; 32 public int16 ymax = int16.MIN; 33 34 public FontData dis; 35 public HeadTable head_table; 36 public HmtxTable hmtx_table; 37 public LocaTable loca_table; 38 public CmapTable cmap_table; // cmap and post is null when inistialized and set in parse method 39 public PostTable post_table; 40 public KernTable kern_table; 41 42 public Gee.ArrayList<uint32> location_offsets; 43 44 // sorted array of glyphs 45 public Gee.ArrayList<GlyphCollection> glyphs; 46 public Gee.ArrayList<GlyfData> glyf_data; 47 48 uint16 max_points = 0; 49 uint16 max_contours = 0; 50 51 public GlyfTable (LocaTable l) { 52 id = "glyf"; 53 loca_table = l; 54 location_offsets = new Gee.ArrayList<uint32> (); 55 glyphs = new Gee.ArrayList<GlyphCollection> (); 56 glyf_data = new Gee.ArrayList<GlyfData> (); 57 } 58 59 public int get_gid (string name) { 60 int i = 0; 61 62 foreach (GlyphCollection g in glyphs) { 63 if (g.get_name () == name) { 64 return i; 65 } 66 i++; 67 } 68 69 warning (@"Glyph $name not found in font."); 70 return -1; 71 } 72 73 public uint16 get_max_contours () { 74 return max_contours; 75 } 76 77 public uint16 get_max_points () { 78 return max_points; 79 } 80 81 public uint16 get_first_char () { 82 return 32; // space 83 } 84 85 public uint16 get_last_char () 86 requires (glyphs.size > 0) { 87 GlyphCollection gc = glyphs.get (glyphs.size - 1); 88 89 for (int i = glyphs.size - 1; i >= 0; i--) { 90 gc = glyphs.get (i); 91 92 if (!gc.is_unassigned ()) { 93 break; 94 } 95 } 96 97 return (uint16) gc.get_unicode_character (); 98 } 99 100 public void process () throws GLib.Error { 101 FontData fd = new FontData (); 102 uint last_len = 0; 103 uint num_glyphs; 104 Glyph g; 105 106 create_glyph_table (); 107 108 num_glyphs = glyphs.size; 109 110 if (glyphs.size == 0) { 111 warning ("No glyphs in glyf table."); 112 } 113 114 foreach (GlyphCollection gc in glyphs) { 115 g = gc.get_current (); 116 // set values for loca table 117 assert (fd.length () % 4 == 0); 118 location_offsets.add (fd.length ()); 119 process_glyph (g, fd); 120 121 printd (@"adding glyph: $(g.name)\n"); 122 printd (@"glyf length: $(fd.length () - last_len)\n"); 123 printd (@"loca fd.length (): $(fd.length ())\n"); 124 125 last_len = fd.length (); 126 } 127 128 location_offsets.add (fd.length ()); // last entry in loca table is special 129 130 // every glyph is padded, no padding to be done here 131 assert (fd.length () % 4 == 0); 132 133 font_data = fd; 134 } 135 136 // necessary in order to have glyphs sorted according to ttf specification 137 public void create_glyph_table () { 138 Glyph g; 139 GlyphCollection gc; 140 GlyphCollection? gcn; 141 Font font = OpenFontFormatWriter.get_current_font (); 142 uint32 index; 143 Gee.ArrayList<GlyphCollection> unassigned_glyphs; 144 bool unassigned; 145 146 // add notdef character and other special characters first 147 glyphs.add (font.get_not_def_character ()); 148 glyphs.add (font.get_null_character ()); 149 glyphs.add (font.get_nonmarking_return ()); 150 glyphs.add (font.get_space ()); 151 152 unassigned_glyphs = new Gee.ArrayList<GlyphCollection> (); 153 154 if (font.get_glyph_index (0) == null) { 155 warning ("No glyphs in font."); 156 } 157 158 // add glyphs 159 for (index = 0; (gcn = font.get_glyph_collection_index (index)) != null; index++) { 160 gc = (!) gcn; 161 gc = gc.copy_deep (); 162 g = gc.get_current (); 163 g.remove_empty_paths (); 164 unassigned = gc.is_unassigned (); 165 166 if (unassigned && gc.get_name () != ".notdef") { 167 unassigned_glyphs.add (gc); 168 } 169 170 if (g.unichar_code <= 27) { // skip control characters 171 continue; 172 } 173 174 if (g.unichar_code == 32) { // skip space 175 continue; 176 } 177 178 if (g.name == ".notdef") { 179 continue; 180 } 181 182 if (!gc.is_unassigned ()) { 183 glyphs.add (gc); 184 } 185 } 186 187 glyphs.sort ((a, b) => { 188 GlyphCollection g1, g2; 189 g1 = (GlyphCollection) a; 190 g2 = (GlyphCollection) b; 191 return (int) (g1.get_unicode_character () - g2.get_unicode_character ()); 192 }); 193 194 foreach (GlyphCollection ug in unassigned_glyphs) { 195 glyphs.add (ug); 196 } 197 } 198 199 public void process_glyph (Glyph g, FontData fd) throws GLib.Error { 200 uint16 end_point; 201 uint16 npoints; 202 int16 ncontours; 203 int16 nflags; 204 int glyph_offset; 205 uint len; 206 uint coordinate_length; 207 GlyfData glyf_data; 208 209 fd.seek_end (); // append glyph 210 211 glyph_offset = (int) fd.length (); 212 213 printd (@"glyph_offset: $(glyph_offset)\n"); 214 215 g.remove_empty_paths (); 216 glyf_data = g.get_ttf_data (); 217 218 this.glyf_data.add (glyf_data); 219 220 if (g.get_visible_paths ().size == 0 || glyf_data.paths.size == 0 || glyf_data.get_ncontours () == 0) { 221 // location_offsets will be equal to location_offset + 1 for 222 // all empty glyphs 223 g.set_empty_ttf (true); 224 return; 225 } 226 227 g.set_empty_ttf (false); 228 229 if (glyf_data.get_ncontours () == 0) { 230 warning (@"No paths in $(g.get_name ()) ($(g.get_hex ())) can be exported."); 231 } 232 233 ncontours = (int16) glyf_data.paths.size; 234 fd.add_short (ncontours); 235 236 // bounding box 237 fd.add_16 (glyf_data.bounding_box_xmin); 238 fd.add_16 (glyf_data.bounding_box_ymin); 239 fd.add_16 (glyf_data.bounding_box_xmax); 240 fd.add_16 (glyf_data.bounding_box_ymax); 241 242 // end points 243 foreach (uint16 end in glyf_data.end_points) { 244 fd.add_u16 (end); 245 } 246 247 fd.add_u16 (0); // instruction length 248 249 uint glyph_header = 12 + ncontours * 2; 250 251 printd (@"next glyf: $(g.name) ($((uint32)g.unichar_code))\n"); 252 printd (@"glyf header length: $(glyph_header)\n"); 253 254 end_point = glyf_data.get_end_point (); 255 ncontours = glyf_data.get_ncontours (); 256 npoints = (ncontours > 0) ? end_point : 0; // +1? 257 258 if (npoints > max_points) { 259 max_points = npoints; 260 } 261 262 if (ncontours > max_contours) { 263 max_contours = ncontours; 264 } 265 266 // flags 267 nflags = glyf_data.get_nflags (); 268 if (unlikely (nflags != npoints)) { 269 print ("glyf table data:\n"); 270 fd.dump (); 271 warning (@"(nflags != npoints) ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours"); 272 } 273 assert (nflags == npoints); 274 275 foreach (uint8 flag in glyf_data.flags) { 276 fd.add_byte (flag); 277 } 278 279 printd (@"flags: $(nflags)\n"); 280 281 // x coordinates 282 foreach (int16 x in glyf_data.coordinate_x) { 283 fd.add_16 (x); 284 } 285 286 // y coordinates 287 foreach (int16 y in glyf_data.coordinate_y) { 288 fd.add_16 (y); 289 } 290 291 len = fd.length (); 292 coordinate_length = fd.length () - nflags - glyph_header; 293 printd (@"coordinate_length: $(coordinate_length)\n"); 294 printd (@"fd.length (): $(fd.length ())\n"); 295 assert (fd.length () > nflags + glyph_header); 296 297 printd (@"glyph_offset: $(glyph_offset)\n"); 298 printd (@"len: $(len)\n"); 299 300 // save bounding box for head table 301 if (glyf_data.bounding_box_xmin < this.xmin) { 302 this.xmin = glyf_data.bounding_box_xmin; 303 } 304 305 if (glyf_data.bounding_box_ymin < this.ymin) { 306 this.ymin = glyf_data.bounding_box_ymin; 307 } 308 309 if (glyf_data.bounding_box_xmax > this.xmax) { 310 this.xmax = glyf_data.bounding_box_xmax; 311 } 312 313 if (glyf_data.bounding_box_ymax > this.ymax) { 314 this.ymax = glyf_data.bounding_box_ymax; 315 } 316 317 printd (@"length before padding: $(fd.length ())\n"); 318 319 // all glyphs needs padding for loca table to be correct 320 while (fd.length () % 4 != 0) { 321 fd.add (0); 322 } 323 printd (@"length after padding: $(fd.length ())\n"); 324 } 325 326 public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error { 327 this.cmap_table = cmap_table; 328 this.post_table = post_table; 329 this.loca_table = loca; 330 this.hmtx_table = hmtx_table; 331 this.head_table = head_table; 332 this.kern_table = kern_table; 333 this.dis = dis; 334 } 335 336 Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error { 337 uint16 component_flags = 0; 338 uint16 glyph_index; 339 int16 arg1 = 0; 340 int16 arg2 = 0; 341 342 F2Dot14 scale; 343 344 F2Dot14 scalex; 345 F2Dot14 scaley; 346 347 F2Dot14 scale01; 348 F2Dot14 scale10; 349 350 Glyph glyph, linked_glyph; 351 Gee.ArrayList<int> x = new Gee.ArrayList<int> (); 352 Gee.ArrayList<int> y = new Gee.ArrayList<int> (); 353 Gee.ArrayList<int> gid = new Gee.ArrayList<int> (); 354 355 double xmin, xmax; 356 double units_per_em = head_table.get_units_per_em (); 357 358 int glid; 359 360 StringBuilder name = new StringBuilder (); 361 name.append_unichar (character); 362 363 glyph = new Glyph (name.str, character); 364 365 do { 366 component_flags = dis.read_ushort (); 367 glyph_index = dis.read_ushort (); 368 369 if ((component_flags & BOTH_ARE_WORDS) > 0) { 370 arg1 = dis.read_short (); 371 arg2 = dis.read_short (); 372 } else if ((component_flags & BOTH_ARE_XY_VALUES) > 0) { 373 arg1 = dis.read_byte (); 374 arg2 = dis.read_byte (); 375 } 376 377 gid.add (glyph_index); 378 x.add (arg1); 379 y.add (arg2); 380 381 // if ((component_flags & RESERVED) > 0) 382 383 if ((component_flags & SCALE) > 0) { 384 scale = dis.read_f2dot14 (); 385 } else if ((component_flags & SCALE_X_Y) > 0) { 386 scalex = dis.read_f2dot14 (); 387 scaley = dis.read_f2dot14 (); 388 } else if ((component_flags & SCALE_WITH_ROTATTION) > 0) { 389 scalex = dis.read_f2dot14 (); 390 scale01 = dis.read_f2dot14 (); 391 scale10 = dis.read_f2dot14 (); 392 scaley = dis.read_f2dot14 (); 393 } 394 395 } while ((component_flags & MORE_COMPONENTS) > 0); 396 397 398 for (int i = 0; i < gid.size; i++) { 399 // compensate xmax ymax with coordinate 400 glid = gid.get (i); 401 402 if (glid == pgid) { 403 warning ("Cannot link a glyph to it self."); 404 continue; 405 } 406 407 linked_glyph = parse_next_glyf (dis, character, glid, out xmin, out xmax, units_per_em); 408 } 409 410 return glyph; 411 } 412 413 Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error { 414 uint16* end_points = null; 415 uint8* instructions = null; 416 uint8* flags = null; 417 int16* xcoordinates = null; 418 int16* ycoordinates = null; 419 420 int npoints = 0; 421 422 int16 ncontours; 423 int16 ixmin; // set boundaries 424 int16 iymin; 425 int16 ixmax; 426 int16 iymax; 427 uint16 ninstructions; 428 429 int16 rxmin = int16.MAX; // real xmin 430 int16 rymin = int16.MAX;; 431 int16 rxmax = int16.MIN; 432 int16 rymax = int16.MIN; 433 434 int nflags; 435 436 Error? error = null; 437 438 uint start, end, len; 439 440 StringBuilder name = new StringBuilder (); 441 name.append_unichar (character); 442 443 xmin = 0; 444 xmax = 0; 445 446 start = loca_table.get_offset (gid); 447 end = loca_table.get_offset (gid + 1); 448 len = start - end; 449 450 dis.seek (offset + start); 451 452 ncontours = dis.read_short (); 453 454 return_val_if_fail (start < end, new Glyph ("")); 455 456 if (ncontours == 0) { 457 warning (@"Zero contours in glyph $(name.str)."); 458 459 // should skip body 460 } 461 462 if (ncontours == -1) { 463 return parse_next_composite_glyf (dis, character, gid); 464 } 465 466 return_val_if_fail (ncontours < len, new Glyph ("")); 467 468 if (ncontours < -1) { 469 warning (@"$ncontours contours in glyf table."); 470 error = new BadFormat.PARSE ("Invalid glyf"); 471 throw error; 472 } 473 474 ixmin = dis.read_short (); 475 iymin = dis.read_short (); 476 ixmax = dis.read_short (); 477 iymax = dis.read_short (); 478 479 end_points = new uint16[ncontours + 1]; 480 for (int i = 0; i < ncontours; i++) { 481 end_points[i] = dis.read_ushort (); // FIXME: mind shot vector is negative 482 483 if (i > 0 && end_points[i] < end_points[i -1]) { 484 warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1]) ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours"); 485 } 486 } 487 488 if (ncontours > 0) { 489 npoints = end_points[ncontours - 1] + 1; 490 } else { 491 npoints = 0; 492 } 493 494 return_val_if_fail (npoints < len, new Glyph.no_lines ("")); 495 496 ninstructions = dis.read_ushort (); 497 498 return_val_if_fail (ninstructions < len, new Glyph.no_lines ("")); 499 500 instructions = new uint8[ninstructions + 1]; 501 uint8 repeat; 502 for (int i = 0; i < ninstructions; i++) { 503 instructions[i] = dis.read_byte (); 504 } 505 506 nflags = 0; 507 flags = new uint8[npoints + 1]; 508 for (int i = 0; i < npoints; i++) { 509 flags[i] = dis.read_byte (); 510 511 if ((flags[i] & CoordinateFlags.REPEAT) > 0) { 512 repeat = dis.read_byte (); 513 514 if (i + repeat >= npoints) { 515 error = new BadFormat.PARSE ("Too many flags in glyf in glyph $(name.str). (i >= ninstructions)."); 516 break; 517 } 518 519 for (int j = 0; j < repeat; j++) { 520 flags[j + i + 1] = flags[i]; 521 } 522 523 nflags += repeat; 524 i += repeat; 525 } 526 527 nflags++; 528 } 529 530 if (nflags != npoints) { 531 warning (@"(nflags != npoints) ($nflags != $npoints) in $(name.str)"); 532 error = new BadFormat.PARSE (@"Wrong number of flags in glyph $(name.str). (nflags != npoints) ($nflags != $npoints)"); 533 } 534 535 warn_if_fail (nflags == npoints); 536 537 printd (@"npoints: $npoints\n"); 538 printd (@"ncontours: $ncontours\n"); 539 printd (@"ninstructions: $ninstructions\n"); 540 printd (@"nflags: $nflags\n"); 541 542 int16 last = 0; 543 xcoordinates = new int16[npoints + 1]; 544 for (int i = 0; i < npoints; i++) { 545 if ((flags[i] & CoordinateFlags.X_SHORT_VECTOR) > 0) { 546 if ((flags[i] & CoordinateFlags.X_SHORT_VECTOR_POSITIVE) > 0) { 547 xcoordinates[i] = last + dis.read_byte (); 548 } else { 549 xcoordinates[i] = last - dis.read_byte (); 550 } 551 } else { 552 if ((flags[i] & CoordinateFlags.X_IS_SAME) > 0) { 553 xcoordinates[i] = last; 554 } else { 555 xcoordinates[i] = last + dis.read_short (); 556 } 557 } 558 559 last = xcoordinates[i]; 560 561 if (last > rxmax) rxmax = last; 562 if (last < rxmin) rxmin = last; 563 564 if (!(ixmin <= last <= ixmax)) { 565 stderr.printf (@"x is out of bounds in glyph $(name.str). ($ixmin <= $last <= $ixmax) char $((uint)character)\n"); 566 } 567 568 if (!(head_table.xmin <= last <= head_table.xmax)) { 569 stderr.printf (@"x is outside of of font bounding box in glyph $(name.str). ($(head_table.xmin) <= $last <= $(head_table.xmax)) char $((uint)character)\n"); 570 } 571 } 572 573 last = 0; 574 ycoordinates = new int16[npoints + 1]; 575 for (int i = 0; i < npoints; i++) { 576 if ((flags[i] & CoordinateFlags.Y_SHORT_VECTOR) > 0) { 577 if ((flags[i] & CoordinateFlags.Y_SHORT_VECTOR_POSITIVE) > 0) { 578 ycoordinates[i] = last + dis.read_byte (); 579 } else { 580 ycoordinates[i] = last - dis.read_byte (); 581 } 582 } else { 583 if ((flags[i] & CoordinateFlags.Y_IS_SAME) > 0) { 584 ycoordinates[i] = last; 585 } else { 586 ycoordinates[i] = last + dis.read_short (); 587 } 588 } 589 590 last = ycoordinates[i]; 591 592 if (last > rymax) rymax = last; 593 if (last < rymin) rymin = last; 594 595 if (!(iymin <= last <= iymax)) { 596 stderr.printf (@"y is out of bounds in glyph $(name.str). ($iymin <= $last <= $iymax) char $((uint)character)\n"); 597 } 598 599 if (!(head_table.ymin <= last <= head_table.ymax)) { 600 stderr.printf (@"y is outside of of font bounding box in glyph $(name.str). ($(head_table.ymin) <= $last <= $(head_table.ymax)) char $((uint)character)\n"); 601 } 602 } 603 604 if (rymin != iymin || rxmin != ixmin || rxmax != ixmax || rymax != iymax) { 605 warning (@"Warning real boundary for glyph does not match boundary set in glyph header for glyph $(name.str)."); 606 stderr.printf (@"ymin: $rymin header: $iymin\n"); 607 stderr.printf (@"xmin: $rxmin header: $ixmin\n"); 608 stderr.printf (@"ymax: $rymax header: $iymax\n"); 609 stderr.printf (@"xmax: $rxmax header: $ixmax\n"); 610 } 611 612 int j = 0; 613 int first_point; 614 int last_point = 0; 615 Glyph glyph; 616 double x, y; 617 618 glyph = new Glyph (name.str, character); 619 620 xmin = ixmin * 1000.0 / units_per_em; 621 xmax = ixmax * 1000.0 / units_per_em; 622 623 for (int i = 0; i < ncontours; i++) { 624 x = 0; 625 y = 0; 626 627 Path path = new Path (); 628 EditPoint edit_point = new EditPoint (); 629 bool prev_is_curve = false; 630 631 first_point = j; 632 last_point = end_points[i]; 633 for (; j <= end_points[i]; j++) { 634 635 if (j >= npoints) { 636 warning (@"j >= npoints in glyph $(name.str). (j: $j, end_points[i]: $(end_points[i]), npoints: $npoints)"); 637 break; 638 } 639 640 x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width 641 y = ycoordinates[j] * 1000.0 / units_per_em; 642 643 if ((flags[j] & CoordinateFlags.ON_PATH) > 0) { 644 // Point 645 edit_point = new EditPoint (); 646 edit_point.set_position (x, y); 647 path.add_point (edit_point); 648 649 if (prev_is_curve) { 650 edit_point.get_left_handle ().set_point_type (PointType.NONE); 651 edit_point.get_left_handle ().length = 0; 652 } else { 653 path.recalculate_linear_handles_for_point (edit_point); 654 } 655 656 prev_is_curve = false; 657 } else { 658 659 if (prev_is_curve) { 660 x = x - (x - edit_point.right_handle.x) / 2; 661 y = y - (y - edit_point.right_handle.y) / 2; 662 663 edit_point = new EditPoint (); 664 edit_point.set_position (x, y); 665 path.add_point (edit_point); 666 } 667 668 x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width 669 y = ycoordinates[j] * 1000.0 / units_per_em; 670 671 edit_point.get_left_handle ().set_point_type (PointType.NONE); 672 edit_point.get_left_handle ().length = 0; 673 674 edit_point.type = PointType.CUBIC; 675 edit_point.get_right_handle ().set_point_type (PointType.CUBIC); 676 edit_point.get_right_handle ().move_to_coordinate (x, y); 677 678 prev_is_curve = true; 679 } 680 } 681 682 // last to first point 683 if (prev_is_curve) { 684 x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width 685 y = ycoordinates[first_point] * 1000.0 / units_per_em; 686 687 x = x - (x - edit_point.right_handle.x) / 2; 688 y = y - (y - edit_point.right_handle.y) / 2; 689 690 edit_point = new EditPoint (); 691 edit_point.set_position (x, y); 692 path.add_point (edit_point); 693 694 x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width 695 y = ycoordinates[first_point] * 1000.0 / units_per_em; 696 697 edit_point.get_left_handle ().set_point_type (PointType.CUBIC); 698 edit_point.get_left_handle ().length = 0; 699 700 edit_point.type = PointType.CUBIC; 701 edit_point.get_right_handle ().set_point_type (PointType.CUBIC); 702 edit_point.get_right_handle ().move_to_coordinate (x, y); 703 } 704 705 // curve last to first 706 x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width 707 y = ycoordinates[first_point] * 1000.0 / units_per_em; 708 edit_point.type = PointType.CUBIC; 709 edit_point.get_right_handle ().set_point_type (PointType.CUBIC); 710 edit_point.get_right_handle ().move_to_coordinate (x, y); 711 712 path.close (); 713 714 glyph.add_path (path); 715 } 716 717 // glyphs with no bounding boxes 718 if (ixmax <= ixmin) { 719 warning (@"Bounding box is bad. (xmax == xmin) ($xmax == $xmin)"); 720 721 var visible_paths = glyph.get_visible_paths (); 722 if (visible_paths.size > 0) { 723 724 Path ps = visible_paths.get (0); 725 726 ps.update_region_boundaries (); 727 xmin = ps.xmin; 728 xmax = ps.xmax; 729 730 foreach (Path p in visible_paths) { 731 p.update_region_boundaries (); 732 733 if (p.xmin < xmin) { 734 xmin = p.xmin; 735 } 736 737 if (p.xmax > xmax) { 738 xmax = p.xmax; 739 } 740 } 741 742 } 743 } 744 745 if (end_points != null) { 746 delete end_points; 747 } 748 749 if (instructions != null) { 750 delete instructions; 751 } 752 753 if (flags != null) { 754 delete flags; 755 } 756 757 if (xcoordinates != null) { 758 delete xcoordinates; 759 } 760 761 if (ycoordinates != null) { 762 delete ycoordinates; 763 } 764 765 if (error != null) { 766 warning ("Failed to parse glyph"); 767 throw (!) error; 768 } 769 770 return glyph; 771 } 772 } 773 774 } 775