The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

load_font.c 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/load_font.c.
Better TTF parsing
1 /* 2 Copyright (C) 2013 2014 2015 2018 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 #include <assert.h> 16 #include <glib.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <stdlib.h> 20 #include <ft2build.h> 21 #include FT_FREETYPE_H 22 #include FT_GLYPH_H 23 #include FT_OPENTYPE_VALIDATE_H 24 #include FT_TRUETYPE_TABLES_H 25 #include FT_SFNT_NAMES_H 26 27 /** Error codes. */ 28 #define OK 0 29 30 /** Point flags. */ 31 #define QUADRATIC_OFF_CURVE 0 32 #define ON_CURVE 1 33 #define CUBIC_CURVE 2 34 #define DOUBLE_CURVE 4 35 #define HIDDEN_CURVE 8 36 37 typedef struct FontFace { 38 FT_Face face; 39 FT_Library library; 40 } FontFace; 41 42 void close_ft_font (FontFace* font); 43 44 /** Convert units per em in font file format to the BirdFont format. */ 45 double get_units (double units_per_em) { 46 return 100.0 / units_per_em; 47 } 48 49 /** @return a non zero value if the point is a part of two off curve 50 * points with a hidden on curve point in between. 51 */ 52 guint is_double_curve (char flag) { 53 return flag & DOUBLE_CURVE; 54 } 55 56 /** @return a non zero value if the point is a cubic off curve point. */ 57 guint is_cubic (char flag) { 58 return flag & CUBIC_CURVE && (flag & ON_CURVE) == 0; 59 } 60 61 /** @return a non zero value if the point is a quadratic off curve point. */ 62 guint is_quadratic (char flag) { 63 return (flag & CUBIC_CURVE) == 0 && (flag & ON_CURVE) == 0; 64 } 65 66 /** @return a non zero value if the point is a on curve point */ 67 guint is_line (char flag) { 68 return flag & ON_CURVE; 69 } 70 71 /** @return a non zero value if the point is a hidden on curve point */ 72 guint is_hidden (char flag) { 73 return flag & HIDDEN_CURVE; 74 } 75 76 /** Convert every second hidden curve to a double curve and keep 77 * other points as on curve points. 78 */ 79 void set_double_curves (char* flag, int length) { 80 int i; 81 guint double_curve = FALSE; 82 83 for (i = 1; i < length; i++) { 84 if (is_line (flag[i])) { 85 double_curve = FALSE; 86 } else if (is_hidden (flag[i])) { 87 if (!double_curve) { 88 if (is_quadratic (flag[i - 1]) && is_quadratic (flag[i + 1])) { 89 flag[i - 1] = DOUBLE_CURVE; 90 flag[i] = HIDDEN_CURVE; 91 flag[i + 1] = DOUBLE_CURVE; 92 double_curve = TRUE; 93 } else { 94 flag[i] = ON_CURVE; 95 double_curve = FALSE; 96 } 97 } else { 98 flag[i] = ON_CURVE; 99 double_curve = FALSE; 100 } 101 } 102 // ignore off curve points 103 } 104 } 105 106 /** @return half the distance between two points. */ 107 double half_way (double prev, double current) { 108 return prev + ((current - prev) / 2.0); 109 } 110 111 /** Remove hidden points. 112 * @return length after removal 113 */ 114 double remove_hidden_points (FT_Vector* points, char* flags, guint length, guint capacity) { 115 int k; 116 int l; 117 118 l = 0; 119 for (k = 0; k < length; k++) { 120 if (!is_hidden (flags[k])) { 121 points[l] = points[k]; 122 flags[l] = flags[k]; 123 l++; 124 } 125 } 126 127 for (k = l; k < capacity; k++) { 128 points[l].x = 0; 129 points[l].y = 0; 130 flags[l] = 0; 131 } 132 133 return l; 134 } 135 136 /** Add extra point where two ore more off curve points follow each other. */ 137 void create_contour (guint unicode, FT_Vector* points, char* flags, int* length, FT_Vector** new_points, char** new_flags, int* err) { 138 // This function has been tested with many fonts. ElsieSwashCaps-Regular 139 // is one of the more interesting fonts I have seen. 140 int i; 141 int j; 142 guint prev_is_curve; 143 guint first_is_curve = FALSE; 144 guint first_normal_off_curve = FALSE; 145 double x = 0; 146 double y = 0; 147 FT_Vector* p; 148 char* f; 149 int len = *length; 150 151 *new_points = malloc (4 * len * sizeof (FT_Vector)); 152 *new_flags = malloc (4 * len * sizeof (char)); 153 154 p = *new_points; 155 f = *new_flags; 156 157 for (i = 0; i < 4 * len; i++) { 158 p[i].x = 0; 159 p[i].y = 0; 160 f[i] = 0; 161 } 162 163 if (len == 0) { 164 return; 165 } 166 167 prev_is_curve = is_quadratic (flags[len - 1]); 168 169 j = 0; 170 i = 0; 171 172 if (len > 2 && is_quadratic (flags[0]) && is_quadratic (flags[1])) { 173 p[j].x = half_way (points[0].x, points[1].x); 174 p[j].y = half_way (points[0].y, points[1].y); 175 176 f[j] = ON_CURVE; 177 prev_is_curve = FALSE; 178 first_is_curve = TRUE; 179 j++; 180 i++; 181 } 182 183 // first point is of curve but it is not part of a double curve 184 if (len > 2 && is_quadratic (flags[0]) && !is_quadratic (flags[1])) { 185 first_normal_off_curve = TRUE; 186 } 187 188 while (i < len) { 189 if (is_quadratic (flags[i])) { 190 if (prev_is_curve && j != 0) { 191 // add a new point if half way between two off curve points 192 x = half_way (points[i].x, p[j - 1].x); 193 y = half_way (points[i].y, p[j - 1].y); 194 195 p[j].x = x; 196 p[j].y = y; 197 198 f[j] = HIDDEN_CURVE; 199 200 j++; 201 } 202 203 if (i == 0) { // A fix for FiraCode Regular 204 i++; 205 continue; 206 } 207 208 f[j] = QUADRATIC_OFF_CURVE; 209 prev_is_curve = TRUE; 210 } else if (is_line (flags[i])) { 211 prev_is_curve = FALSE; 212 f[j] = ON_CURVE; 213 } else if (is_cubic (flags[i])) { 214 prev_is_curve = FALSE; 215 f[j] = CUBIC_CURVE; 216 } else { 217 g_warning ("WARNING invalid point flags: %d index: %d.\n", flags[i], i); 218 prev_is_curve = FALSE; 219 f[j] = ON_CURVE; 220 } 221 222 p[j] = points[i]; 223 j++; 224 i++; 225 } 226 227 // last to first point 228 if (first_is_curve && !prev_is_curve && is_quadratic (flags[i])) { 229 p[j] = points[i]; 230 f[j] = QUADRATIC_OFF_CURVE; 231 j++; 232 i++; 233 234 p[j].x = half_way (p[j - 1].x, points[0].x); 235 p[j].y = half_way (p[j - 1].y, points[0].y); 236 f[j] = ON_CURVE; // FIXME 237 prev_is_curve = FALSE; 238 j++; 239 i++; 240 241 p[j] = points[0]; 242 f[j] = QUADRATIC_OFF_CURVE; 243 j++; 244 i++; 245 246 p[j] = p[0]; 247 f[j] = f[0]; 248 j++; 249 } else if (first_is_curve && !prev_is_curve && is_line (flags[i])) { 250 p[j] = points[i]; 251 f[j] = ON_CURVE; 252 j++; 253 i++; 254 255 p[j] = points[0]; 256 f[j] = QUADRATIC_OFF_CURVE; 257 j++; 258 i++; 259 260 p[j] = p[0]; 261 f[j] = f[0]; 262 j++; 263 } else if (first_is_curve && prev_is_curve && is_quadratic (flags[i])) { 264 x = half_way (p[j - 1].x, points[i].x); 265 y = half_way (p[j - 1].y, points[i].y); 266 p[j].x = x; 267 p[j].y = y; 268 f[j] = HIDDEN_CURVE; 269 j++; 270 271 p[j] = points[i]; 272 f[j] = flags[i]; 273 j++; 274 i++; 275 276 p[j].x = half_way (p[j - 1].x, points[0].x); 277 p[j].y = half_way (p[j - 1].y, points[0].y); 278 f[j] = HIDDEN_CURVE; 279 prev_is_curve = FALSE; 280 281 j++; 282 i++; 283 284 p[j] = points[0]; 285 f[j] = QUADRATIC_OFF_CURVE; 286 j++; 287 288 p[j] = p[0]; 289 f[j] = ON_CURVE; 290 j++; 291 292 prev_is_curve = TRUE; 293 } else if (prev_is_curve && (flags[0] & ON_CURVE) == 0) { 294 if (is_quadratic (f[j - 1]) && is_quadratic (flags[i])) { 295 x = half_way (p[j - 1].x, points[i].x); 296 y = half_way (p[j - 1].y, points[i].y); 297 p[j].x = x; 298 p[j].y = y; 299 f[j] = HIDDEN_CURVE; 300 j++; 301 } 302 303 p[j] = points[i]; 304 if (is_line (flags[i])) { 305 f[j] = ON_CURVE; 306 } else { 307 f[j] = QUADRATIC_OFF_CURVE; 308 } 309 j++; 310 311 if (is_quadratic (f[0]) && is_quadratic (flags[0])) { 312 x = half_way (p[j - 1].x, points[0].x); 313 y = half_way (p[j - 1].y, points[0].y); 314 p[j].x = x; 315 p[j].y = y; 316 f[j] = HIDDEN_CURVE; 317 j++; 318 } 319 320 x = points[0].x; 321 y = points[0].y; 322 323 p[j].x = x; 324 p[j].y = y; 325 326 f[j] = QUADRATIC_OFF_CURVE; 327 j++; 328 } else if (prev_is_curve && is_quadratic (flags[i])) { 329 x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0); 330 y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0); 331 p[j].x = x; 332 p[j].y = y; 333 f[j] = HIDDEN_CURVE; 334 j++; 335 336 p[j] = points[i]; 337 f[j] = QUADRATIC_OFF_CURVE; 338 j++; 339 i++; 340 341 if (is_quadratic (f[0])) { 342 x = half_way (p[j - 1].x, points[i].x); 343 y = half_way (p[j - 1].y, points[i].y); 344 p[j].x = x; 345 p[j].y = y; 346 f[j] = HIDDEN_CURVE; 347 j++; 348 349 p[j] = p[0]; 350 f[j] = f[0]; 351 j++; 352 } else { 353 p[j] = p[0]; 354 f[j] = f[0]; 355 j++; 356 } 357 358 prev_is_curve = TRUE; 359 } else { 360 p[j] = points[i]; 361 if (is_quadratic (flags[i])) 362 f[j] = QUADRATIC_OFF_CURVE; 363 else 364 f[j] = ON_CURVE; 365 j++; 366 367 p[j] = p[0]; 368 if (is_quadratic (flags[i])) 369 f[j] = QUADRATIC_OFF_CURVE; 370 else 371 f[j] = ON_CURVE; 372 j++; 373 } 374 375 set_double_curves (f, j); 376 *length = remove_hidden_points (p, f, j, 2 * len); 377 } 378 379 /** Get path data in .bf format. See BirdFontFile.get_point_data () for 380 * format specification. 381 */ 382 GString* get_bf_contour_data (guint unicode, FT_Vector* points, char* flags, int length, double units_per_em, int* err) { 383 GString* bf = g_string_new (""); 384 GString* contour; 385 int i = 0; 386 FT_Vector* new_points; 387 char* new_flags; 388 double x0, x1, x2; 389 double y0, y1, y2; 390 gchar coordinate_buffer[80]; 391 gchar* coordinate = (gchar*) &coordinate_buffer; 392 double units = get_units (units_per_em); 393 guint prev_is_curve; 394 int points_length; 395 396 if (length == 0) { 397 return bf; 398 } 399 400 points_length = length; 401 create_contour (unicode, points, flags, &length, &new_points, &new_flags, err); 402 403 x0 = new_points[0].x * units; 404 y0 = new_points[0].y * units; 405 406 g_string_printf (bf, "S "); 407 408 g_ascii_formatd (coordinate, 80, "%f", x0); 409 g_string_append (bf, coordinate); 410 g_string_append (bf, ","); 411 412 g_ascii_formatd (coordinate, 80, "%f", y0); 413 g_string_append (bf, coordinate); 414 415 i = 1; 416 while (i < length) { 417 contour = g_string_new (""); 418 419 if (is_hidden (new_flags[i])) { 420 // Skip it 421 g_string_append (contour, ""); 422 i++; 423 } else if (is_cubic (new_flags[i])) { 424 x0 = new_points[i].x * units; 425 y0 = new_points[i].y * units; 426 x1 = new_points[i+1].x * units; 427 y1 = new_points[i+1].y * units; 428 x2 = new_points[i+2].x * units; 429 y2 = new_points[i+2].y * units; 430 431 g_string_printf (contour, " C "); 432 433 g_ascii_formatd (coordinate, 80, "%f", x0); 434 g_string_append (contour, coordinate); 435 g_string_append (contour, ","); 436 437 g_ascii_formatd (coordinate, 80, "%f", y0); 438 g_string_append (contour, coordinate); 439 g_string_append (contour, " "); 440 441 g_ascii_formatd (coordinate, 80, "%f", x1); 442 g_string_append (contour, coordinate); 443 g_string_append (contour, ","); 444 445 g_ascii_formatd (coordinate, 80, "%f", y1); 446 g_string_append (contour, coordinate); 447 g_string_append (contour, " "); 448 449 g_ascii_formatd (coordinate, 80, "%f", x2); 450 g_string_append (contour, coordinate); 451 g_string_append (contour, ","); 452 453 g_ascii_formatd (coordinate, 80, "%f", y2); 454 g_string_append (contour, coordinate); 455 456 i += 3; 457 } else if (is_double_curve (new_flags[i])) { 458 x0 = new_points[i].x * units; 459 y0 = new_points[i].y * units; 460 x1 = new_points[i+1].x * units; 461 y1 = new_points[i+1].y * units; 462 x2 = new_points[i+2].x * units; 463 y2 = new_points[i+2].y * units; 464 465 g_string_printf (contour, " D "); 466 467 g_ascii_formatd (coordinate, 80, "%f", x0); 468 g_string_append (contour, coordinate); 469 g_string_append (contour, ","); 470 471 g_ascii_formatd (coordinate, 80, "%f", y0); 472 g_string_append (contour, coordinate); 473 g_string_append (contour, " "); 474 475 g_ascii_formatd (coordinate, 80, "%f", x1); 476 g_string_append (contour, coordinate); 477 g_string_append (contour, ","); 478 479 g_ascii_formatd (coordinate, 80, "%f", y1); 480 g_string_append (contour, coordinate); 481 g_string_append (contour, " "); 482 483 g_ascii_formatd (coordinate, 80, "%f", x2); 484 g_string_append (contour, coordinate); 485 g_string_append (contour, ","); 486 487 g_ascii_formatd (coordinate, 80, "%f", y2); 488 g_string_append (contour, coordinate); 489 490 i += 3; 491 } else if (is_quadratic (new_flags[i])) { 492 x0 = new_points[i].x * units; 493 y0 = new_points[i].y * units; 494 x1 = new_points[i+1].x * units; 495 y1 = new_points[i+1].y * units; 496 497 g_string_printf (contour, " Q "); 498 499 g_ascii_formatd (coordinate, 80, "%f", x0); 500 g_string_append (contour, coordinate); 501 g_string_append (contour, ","); 502 503 g_ascii_formatd (coordinate, 80, "%f", y0); 504 g_string_append (contour, coordinate); 505 g_string_append (contour, " "); 506 507 g_ascii_formatd (coordinate, 80, "%f", x1); 508 g_string_append (contour, coordinate); 509 g_string_append (contour, ","); 510 511 g_ascii_formatd (coordinate, 80, "%f", y1); 512 g_string_append (contour, coordinate); 513 514 i += 2; 515 } else if (is_line (new_flags[i])) { 516 x0 = new_points[i].x * units; 517 y0 = new_points[i].y * units; 518 519 g_string_printf (contour, " L "); 520 521 g_ascii_formatd (coordinate, 80, "%f", x0); 522 g_string_append (contour, coordinate); 523 g_string_append (contour, ","); 524 525 g_ascii_formatd (coordinate, 80, "%f", y0); 526 g_string_append (contour, coordinate); 527 528 i += 1; 529 } else { 530 contour = g_string_new (""); 531 g_warning ("WARNING Can't parse outline.\n"); 532 *err = 1; 533 i++; 534 } 535 536 g_string_append (bf, contour->str); 537 g_string_free (contour, TRUE); 538 } 539 540 free (new_points); 541 free (new_flags); 542 543 return bf; 544 } 545 546 /** Get path element in .bf format. */ 547 GString* get_bf_path (guint unicode, FT_Face face, double units_per_em, int* err) { 548 GString* bf = g_string_new (""); 549 GString* contour; 550 FT_Error error; 551 int i; 552 int start; 553 int end; 554 555 if (face->glyph->outline.n_points == 0) { 556 return bf; 557 } 558 559 start = 0; 560 for (i = 0; i < face->glyph->outline.n_contours; i++) { 561 end = face->glyph->outline.contours [i]; 562 contour = get_bf_contour_data (unicode, face->glyph->outline.points + start, face->glyph->outline.tags + start, end - start, units_per_em, err); 563 g_string_append_printf (bf, "\t\t<path data=\"%s\" />\n", contour->str); 564 g_string_free (contour, TRUE); 565 start = face->glyph->outline.contours [i] + 1; 566 } 567 568 return bf; 569 } 570 571 FontFace* open_font (const char* file) { 572 FT_Library library = NULL; 573 FT_Face face = NULL; 574 int error; 575 FontFace* font; 576 577 error = FT_Init_FreeType (&library); 578 if (error != OK) { 579 printf ("Freetype init error %d.\n", error); 580 return NULL; 581 } 582 583 error = FT_New_Face (library, file, 0, &face); 584 if (error) { 585 if (FT_Done_FreeType (library) != 0) { 586 g_warning ("Can't close freetype."); 587 } 588 589 g_warning ("Freetype font face error %d\n", error); 590 return NULL; 591 } 592 593 font = malloc (sizeof (FontFace)); 594 font->face = face; 595 font->library = library; 596 597 error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); 598 if (error) { 599 g_warning ("Freetype can not use Unicode, error: %d\n", error); 600 close_ft_font (font); 601 return NULL; 602 } 603 604 return font; 605 } 606 607 void close_ft_font (FontFace* font) { 608 if (font != NULL) { 609 if (font->face != NULL) { 610 if (FT_Done_Face (font->face) != 0) { 611 g_warning ("Can't close font."); 612 } 613 font->face = NULL; 614 } 615 616 if (font->library != NULL) { 617 if (FT_Done_FreeType (font->library) != 0) { 618 g_warning ("Can't close freetype."); 619 } 620 font->library = NULL; 621 } 622 623 free (font); 624 } 625 } 626 627 GString* load_glyph (FontFace* font, guint unicode) { 628 GString* glyph; 629 GString* paths; 630 int err = OK; 631 int gid; 632 FT_ULong charcode = (FT_ULong) unicode; 633 double units; 634 gchar line_position_buffer[80]; 635 gchar* line_position = (gchar*) &line_position_buffer; 636 FT_Error error; 637 638 if (font == NULL || font->face == NULL || font->library == NULL) { 639 printf ("WARNING No font in load_glyph"); 640 return NULL; 641 } 642 643 gid = FT_Get_Char_Index (font->face, charcode); 644 645 if (gid == 0) { 646 return NULL; 647 } 648 649 units = get_units (font->face->units_per_EM); 650 651 glyph = g_string_new ("<font>"); 652 653 g_string_append_printf (glyph, "<horizontal>\n"); 654 655 g_ascii_formatd (line_position, 80, "%f", font->face->ascender * units); 656 g_string_append_printf (glyph, "\t<top_limit>%s</top_limit>\n", line_position); 657 658 g_string_append_printf (glyph, "\t<base_line>0</base_line>\n"); 659 660 g_ascii_formatd (line_position, 80, "%f", font->face->descender * units); 661 g_string_append_printf (glyph, "\t<bottom_limit>%s</bottom_limit>\n", line_position); 662 663 g_string_append_printf (glyph, "</horizontal>\n"); 664 665 error = FT_Load_Glyph(font->face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 666 667 if (error != 0) { 668 printf ("WARNING Failed to load glyph."); 669 g_string_free (glyph, TRUE); 670 return NULL; 671 } 672 673 paths = get_bf_path (unicode, font->face, font->face->units_per_EM, &err); 674 675 if (err != OK) { 676 printf ("WARNING Can't load glyph."); 677 g_string_free (glyph, TRUE); 678 g_string_free (paths, TRUE); 679 return NULL; 680 } 681 682 g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode); 683 g_string_append_printf (glyph, "\t<selected id=\"0\" />\n"); 684 g_string_append_printf (glyph, "\t<glyph id=\"0\" left=\"%f\" right=\"%f\">\n", 685 0.0, font->face->glyph->metrics.horiAdvance * units); 686 687 g_string_append_printf (glyph, "%s", paths->str); 688 g_string_append_printf (glyph, "%s", "\t</glyph>"); 689 g_string_append_printf (glyph, "%s", "\t</collection>"); 690 g_string_append_printf (glyph, "%s", "</font>"); 691 692 g_string_free (paths, TRUE); 693 694 if (err != OK) { 695 g_warning ("Can't load glyph data."); 696 } 697 698 return glyph; 699 } 700 701 /** Get all character codes for a glyph. 702 * @gid glyph id 703 * @return array of character codes 704 */ 705 FT_ULong* get_charcodes (FT_Face face, FT_UInt gid) { 706 FT_ULong charcode = 0; 707 FT_UInt gindex; 708 const int MAX = 255; 709 FT_ULong* result = malloc (sizeof (FT_ULong) * (MAX + 1)); 710 guint results = 0; 711 // TODO: find the lookup function in freetype 712 713 charcode = FT_Get_First_Char (face, &gindex); 714 715 while (gindex != 0) { 716 717 if (results >= MAX) { 718 g_warning ("Too many code points in font for one GID"); 719 break; 720 } 721 722 charcode = FT_Get_Next_Char (face, charcode, &gindex); 723 if (gindex == gid && charcode != 0) { 724 result[results] = charcode; 725 results++; 726 } 727 } 728 729 if (results == 0) { 730 g_warning ("Can not find unicode value for gid %d.", gid); 731 } 732 733 result[results] = 0; 734 results++; 735 736 return result; 737 } 738 739 /** Height of letter. */ 740 int get_height (FT_Face face, guint unichar) { 741 int error; 742 FT_ULong charcode = (FT_ULong) unichar; 743 FT_UInt index = FT_Get_Char_Index (face, charcode); 744 745 error = FT_Load_Glyph (face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 746 if (error) { 747 g_warning ("Failed to obtain height. (%d)\n", error); 748 return 0; 749 } 750 751 return (int) face->glyph->metrics.height; 752 } 753 754 /** Height of lower case letters. */ 755 int get_xheight (FT_Face face) { 756 return get_height (face, 'x'); 757 } 758 759 /** Height of upper case letters. */ 760 int get_top (FT_Face face) { 761 return get_height (face, 'X'); 762 } 763 /** Obtain descender. */ 764 int get_descender (FT_Face face) { 765 int error; 766 FT_BBox bbox; 767 FT_ULong charcode = (FT_ULong) 'g'; 768 FT_UInt index = FT_Get_Char_Index (face, charcode); 769 FT_Glyph glyph; 770 771 error = FT_Load_Glyph (face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 772 if (error) { 773 g_warning ("Failed to obtain descender. (%d)\n", error); 774 return 0; 775 } 776 777 FT_Get_Glyph (face->glyph, &glyph); 778 FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_UNSCALED, &bbox); 779 780 return (int) bbox.yMin; 781 } 782 783 /** Append name table data to a gstring. */ 784 void append_description (GString* str, FT_SfntName* name_table_data) { 785 gchar* utf8_str; 786 gsize read, written; 787 GError* error = NULL; 788 789 if (name_table_data->encoding_id == 0) { // mac roman 790 utf8_str = g_convert (name_table_data->string, name_table_data->string_len, "utf-8", "macintosh", &read, &written, &error); 791 792 if (error == NULL) { 793 g_string_append (str, g_markup_escape_text (utf8_str, -1)); 794 g_free (utf8_str); 795 } else { 796 g_warning ("Error in append_description: %s\n", error->message); 797 g_error_free (error); 798 } 799 } else if (name_table_data->encoding_id == 1) { // windows unicode 800 utf8_str = g_convert (name_table_data->string, name_table_data->string_len, "utf-8", "ucs-2be", &read, &written, &error); 801 802 if (error == NULL) { 803 g_string_append (str, g_markup_escape_text (utf8_str, -1)); 804 g_free (utf8_str); 805 } else { 806 g_warning ("Error in append_description: %s\n", error->message); 807 g_error_free (error); 808 } 809 } else { 810 g_warning ("Encoding %u is not supported for platform %d.\n", name_table_data->encoding_id, name_table_data->platform_id); 811 } 812 } 813 814 int getIndexForNameIdEncoding (FT_Face face, int id, int encoding) { 815 FT_SfntName name_table_data; 816 FT_Error error; 817 int c = FT_Get_Sfnt_Name_Count (face); 818 int i = 0; 819 while (i < c){ 820 error = FT_Get_Sfnt_Name (face, i, &name_table_data); 821 822 if (error == 0 823 && name_table_data.name_id == id 824 && name_table_data.encoding_id == encoding) { 825 return i; 826 } 827 828 i++; 829 } 830 831 return -1; 832 } 833 834 int getIndexForNameId (FT_Face face, int id) { 835 int i = getIndexForNameIdEncoding (face, id, 1); 836 837 if (i != -1) { 838 return i; 839 840 } 841 842 return getIndexForNameIdEncoding (face, id, 0); 843 } 844 845 /** Convert font to bf format. 846 * @param face freetype font face 847 * @param err error code 848 * @return xml representation of a bf font 849 */ 850 GString* get_bf_font (FT_Face face, char* file, int* err) { 851 GString* bf = g_string_new (""); 852 GString* bf_data; 853 gchar* kerning; 854 GString* glyph; 855 FT_Error error; 856 FT_Long i; 857 FT_ULong charcode; 858 FT_UInt gid; 859 FT_SfntName name_table_data; 860 double units_per_em; 861 double units; 862 gchar line_position_buffer[80]; 863 gchar* line_position = (gchar*) &line_position_buffer; 864 int name_index; 865 double gap; 866 867 *err = OK; 868 869 units_per_em = face->units_per_EM; 870 units = get_units (units_per_em); 871 872 if (face == NULL) { 873 return bf; 874 } 875 876 g_string_append (bf, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n"); 877 g_string_append (bf, "<font>\n"); 878 879 g_string_append_printf (bf, "<postscript_name>%s</postscript_name>\n", g_markup_escape_text (FT_Get_Postscript_Name(face), -1)); 880 g_string_append_printf (bf, "<name>%s</name>\n", g_markup_escape_text (face->family_name, -1)); 881 882 if (face->style_name != NULL) { 883 g_string_append_printf (bf, "<subfamily>%s</subfamily>\n", g_markup_escape_text (face->style_name, -1)); 884 } else { 885 g_string_append_printf (bf, "<subfamily></subfamily>\n"); 886 } 887 888 // full name 889 name_index = getIndexForNameId (face, 4); 890 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 891 g_string_append (bf, "<full_name>"); 892 append_description (bf, &name_table_data); 893 g_string_append (bf, "</full_name>\n"); 894 } else { 895 g_string_append (bf, "<full_name></full_name>\n"); 896 } 897 898 // unique identifier 899 name_index = getIndexForNameId (face, 3); 900 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 901 g_string_append (bf, "<unique_identifier>"); 902 append_description (bf, &name_table_data); 903 g_string_append (bf, "</unique_identifier>\n"); 904 } else { 905 g_string_append (bf, "<unique_identifier></unique_identifier>\n"); 906 } 907 908 // version 909 name_index = getIndexForNameId (face, 5); 910 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 911 g_string_append (bf, "<version>"); 912 append_description (bf, &name_table_data); 913 g_string_append (bf, "</version>\n"); 914 } else { 915 g_string_append (bf, "<version></version>\n"); 916 } 917 918 name_index = getIndexForNameId (face, 10); 919 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { // description 920 g_string_append (bf, "<description>"); 921 append_description (bf, &name_table_data); 922 g_string_append (bf, "</description>\n"); 923 } else { 924 g_string_append (bf, "<description></description>\n"); 925 } 926 927 // copyright 928 name_index = getIndexForNameId (face, 0); 929 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 930 g_string_append (bf, "<copyright>"); 931 append_description (bf, &name_table_data); 932 g_string_append (bf, "</copyright>\n"); 933 } else { 934 g_string_append (bf, "<copyright></copyright>\n"); 935 } 936 937 name_index = getIndexForNameId (face, 7); 938 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 939 g_string_append (bf, "<trademark>"); 940 append_description (bf, &name_table_data); 941 g_string_append (bf, "</trademark>\n"); 942 } else { 943 g_string_append (bf, "<trademark></trademark>\n"); 944 } 945 946 name_index = getIndexForNameId (face, 8); 947 if (name_index != -1 && FT_Get_Sfnt_Name (face, 8, &name_table_data) == 0) { 948 g_string_append (bf, "<manefacturer>"); 949 append_description (bf, &name_table_data); 950 g_string_append (bf, "</manefacturer>\n"); 951 } else { 952 g_string_append (bf, "<manefacturer></manefacturer>\n"); 953 } 954 955 name_index = getIndexForNameId (face, 9); 956 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 957 g_string_append (bf, "<designer>"); 958 append_description (bf, &name_table_data); 959 g_string_append (bf, "</designer>\n"); 960 } else { 961 g_string_append (bf, "<designer></designer>\n"); 962 } 963 964 name_index = getIndexForNameId (face, 11); 965 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 966 g_string_append (bf, "<vendor_url>"); 967 append_description (bf, &name_table_data); 968 g_string_append (bf, "</vendor_url>\n"); 969 } else { 970 g_string_append (bf, "<vendor_url></vendor_url>\n"); 971 } 972 973 name_index = getIndexForNameId (face, 12); 974 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 975 g_string_append (bf, "<designer_url>"); 976 append_description (bf, &name_table_data); 977 g_string_append (bf, "</designer_url>\n"); 978 } else { 979 g_string_append (bf, "<designer_url></designer_url>\n"); 980 } 981 982 name_index = getIndexForNameId (face, 13); 983 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 984 g_string_append (bf, "<license>"); 985 append_description (bf, &name_table_data); 986 g_string_append (bf, "</license>\n"); 987 } else { 988 g_string_append (bf, "<license></license>\n"); 989 } 990 991 name_index = getIndexForNameId (face, 14); 992 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 993 g_string_append (bf, "<license_url>"); 994 append_description (bf, &name_table_data); 995 g_string_append (bf, "</license_url>\n"); 996 } else { 997 g_string_append (bf, "<license_url></license_url>\n"); 998 } 999 1000 g_string_append_printf (bf, "<backup>%s</backup>\n", g_markup_escape_text (file, -1)); 1001 1002 1003 g_string_append_printf (bf, "<horizontal>\n"); 1004 1005 g_ascii_formatd (line_position, 80, "%f", face->ascender * units); 1006 g_string_append_printf (bf, "\t<top_limit>%s</top_limit>\n", line_position); 1007 1008 g_ascii_formatd (line_position, 80, "%f", get_top (face) * units); 1009 g_string_append_printf (bf, "\t<top_position>%s</top_position>\n", line_position); 1010 1011 g_ascii_formatd (line_position, 80, "%f", get_xheight (face) * units); 1012 g_string_append_printf (bf, "\t<x-height>%s</x-height>\n", line_position); 1013 1014 g_string_append_printf (bf, "\t<base_line>0</base_line>\n"); 1015 1016 g_ascii_formatd (line_position, 80, "%f", get_descender (face) * units); 1017 g_string_append_printf (bf, "\t<bottom_position>%s</bottom_position>\n", line_position); 1018 1019 g_ascii_formatd (line_position, 80, "%f", face->descender * units); 1020 g_string_append_printf (bf, "\t<bottom_limit>%s</bottom_limit>\n", line_position); 1021 1022 gap = (face->height - (face->ascender - face->descender)) * units; 1023 g_ascii_formatd (line_position, 80, "%f", gap); 1024 g_string_append_printf (bf, "\t<line_gap>%s</line_gap>\n", line_position); 1025 1026 g_string_append_printf (bf, "</horizontal>\n"); 1027 1028 // space character 1029 gid = FT_Get_Char_Index (face, ' '); 1030 if (gid != 0) { 1031 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 1032 g_string_append_printf (bf, "<collection unicode=\"U+20\">\n"); 1033 g_string_append_printf (bf, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 1034 1035 bf_data = get_bf_path (charcode, face, units_per_em, err); 1036 g_string_append (bf, bf_data->str); 1037 1038 g_string_append (bf, "\t</glyph>\n"); 1039 g_string_append_printf (bf, "</collection>\n"); 1040 } 1041 1042 // glyph outlines 1043 for (i = 0; i < face->num_glyphs; i++) { 1044 error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 1045 if (error) { 1046 g_warning ("Freetype failed to load glyph %d.\n", (int)i); 1047 g_warning ("FT_Load_Glyph error %d\n", error); 1048 *err = 1; 1049 return bf; 1050 } 1051 1052 if (face->glyph->format != ft_glyph_format_outline) { 1053 g_warning ("Freetype error no outline found in glyph.\n"); 1054 *err = 1; 1055 return bf; 1056 } 1057 1058 FT_ULong* charcodes = get_charcodes (face, i); 1059 int charcode_index = 0; 1060 1061 while (TRUE) { 1062 charcode = charcodes[charcode_index]; 1063 1064 if (charcode == 0) { 1065 free (charcodes); 1066 charcodes = NULL; 1067 break; 1068 } 1069 1070 glyph = g_string_new (""); 1071 1072 if (charcode > 32) { // not control character 1073 g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode); 1074 g_string_append_printf (glyph, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 1075 1076 bf_data = get_bf_path (charcode, face, units_per_em, err); 1077 g_string_append (glyph, bf_data->str); 1078 1079 g_string_append (glyph, "\t</glyph>\n"); 1080 g_string_append_printf (glyph, "</collection>\n"); 1081 } else { 1082 g_warning ("Ignoring control character, %d.", (guint)charcode); 1083 } 1084 1085 g_string_append (bf, glyph->str); 1086 g_string_free (glyph, TRUE); 1087 1088 charcode_index++; 1089 } 1090 } 1091 1092 bird_font_open_font_format_reader_append_kerning (bf, file); 1093 1094 g_string_append (bf, "</font>\n"); 1095 1096 return bf; 1097 } 1098 1099 /** Load typeface with freetype2 and return the result as a bf font. 1100 * Parameter err will be set to non zero vaule if an error occurs. 1101 */ 1102 GString* load_freetype_font (char* file, int* err) { 1103 GString* bf = NULL; 1104 1105 FT_Library library; 1106 FT_Face face; 1107 int error; 1108 FT_Glyph glyph; 1109 FT_UInt glyph_index; 1110 1111 error = FT_Init_FreeType (&library); 1112 if (error != OK) { 1113 g_warning ("Freetype init error %d.\n", error); 1114 *err = error; 1115 return bf; 1116 } 1117 1118 error = FT_New_Face (library, file, 0, &face); 1119 if (error) { 1120 g_warning ("Freetype font face error %d\n", error); 1121 *err = error; 1122 return bf; 1123 } 1124 1125 error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); 1126 if (error) { 1127 g_warning ("Freetype can not use Unicode, error: %d\n", error); 1128 *err = error; 1129 return bf; 1130 } 1131 1132 error = FT_Set_Char_Size (face, 0, 800, 300, 300); 1133 if (error) { 1134 g_warning ("Freetype FT_Set_Char_Size failed, error: %d.\n", error); 1135 *err = error; 1136 return bf; 1137 } 1138 1139 bf = get_bf_font (face, file, &error); 1140 if (error != OK) { 1141 g_warning ("Failed to parse font.\n"); 1142 *err = error; 1143 return bf; 1144 } 1145 1146 FT_Done_Face ( face ); 1147 FT_Done_FreeType( library ); 1148 1149 *err = OK; 1150 return bf; 1151 } 1152 1153