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