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