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