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