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 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 f[j] = QUADRATIC_OFF_CURVE; 204 prev_is_curve = TRUE; 205 } else if (is_line (flags[i])) { 206 prev_is_curve = FALSE; 207 f[j] = ON_CURVE; 208 } else if (is_cubic (flags[i])) { 209 prev_is_curve = FALSE; 210 f[j] = CUBIC_CURVE; 211 } else { 212 g_warning ("WARNING invalid point flags: %d index: %d.\n", flags[i], i); 213 prev_is_curve = FALSE; 214 f[j] = ON_CURVE; 215 } 216 217 p[j] = points[i]; 218 j++; 219 i++; 220 } 221 222 // last to first point 223 if (first_is_curve && !prev_is_curve && is_quadratic (flags[i])) { 224 p[j] = points[i]; 225 f[j] = QUADRATIC_OFF_CURVE; 226 j++; 227 i++; 228 229 p[j].x = half_way (p[j - 1].x, points[0].x); 230 p[j].y = half_way (p[j - 1].y, points[0].y); 231 f[j] = ON_CURVE; // FIXME 232 prev_is_curve = FALSE; 233 j++; 234 i++; 235 236 p[j] = points[0]; 237 f[j] = QUADRATIC_OFF_CURVE; 238 j++; 239 i++; 240 241 p[j] = p[0]; 242 f[j] = f[0]; 243 j++; 244 } else if (first_is_curve && !prev_is_curve && is_line (flags[i])) { 245 p[j] = points[i]; 246 f[j] = ON_CURVE; 247 j++; 248 i++; 249 250 p[j] = points[0]; 251 f[j] = QUADRATIC_OFF_CURVE; 252 j++; 253 i++; 254 255 p[j] = p[0]; 256 f[j] = f[0]; 257 j++; 258 } else if (first_is_curve && prev_is_curve && is_quadratic (flags[i])) { 259 x = half_way (p[j - 1].x, points[i].x); 260 y = half_way (p[j - 1].y, points[i].y); 261 p[j].x = x; 262 p[j].y = y; 263 f[j] = HIDDEN_CURVE; 264 j++; 265 266 p[j] = points[i]; 267 f[j] = flags[i]; 268 j++; 269 i++; 270 271 p[j].x = half_way (p[j - 1].x, points[0].x); 272 p[j].y = half_way (p[j - 1].y, points[0].y); 273 f[j] = HIDDEN_CURVE; 274 prev_is_curve = FALSE; 275 276 j++; 277 i++; 278 279 p[j] = points[0]; 280 f[j] = QUADRATIC_OFF_CURVE; 281 j++; 282 283 p[j] = p[0]; 284 f[j] = ON_CURVE; 285 j++; 286 287 prev_is_curve = TRUE; 288 } else if (prev_is_curve && (flags[0] & ON_CURVE) == 0) { 289 if (is_quadratic (f[j - 1]) && is_quadratic (flags[i])) { 290 x = half_way (p[j - 1].x, points[i].x); 291 y = half_way (p[j - 1].y, points[i].y); 292 p[j].x = x; 293 p[j].y = y; 294 f[j] = HIDDEN_CURVE; 295 j++; 296 } 297 298 p[j] = points[i]; 299 if (is_line (flags[i])) { 300 f[j] = ON_CURVE; 301 } else { 302 f[j] = QUADRATIC_OFF_CURVE; 303 } 304 j++; 305 306 if (is_quadratic (f[0]) && is_quadratic (flags[0])) { 307 x = half_way (p[j - 1].x, points[0].x); 308 y = half_way (p[j - 1].y, points[0].y); 309 p[j].x = x; 310 p[j].y = y; 311 f[j] = HIDDEN_CURVE; 312 j++; 313 } 314 315 x = points[0].x; 316 y = points[0].y; 317 318 p[j].x = x; 319 p[j].y = y; 320 321 f[j] = QUADRATIC_OFF_CURVE; 322 j++; 323 } else if (prev_is_curve && is_quadratic (flags[i])) { 324 x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0); 325 y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0); 326 p[j].x = x; 327 p[j].y = y; 328 f[j] = HIDDEN_CURVE; 329 j++; 330 331 p[j] = points[i]; 332 f[j] = QUADRATIC_OFF_CURVE; 333 j++; 334 i++; 335 336 if (is_quadratic (f[0])) { 337 x = half_way (p[j - 1].x, points[i].x); 338 y = half_way (p[j - 1].y, points[i].y); 339 p[j].x = x; 340 p[j].y = y; 341 f[j] = HIDDEN_CURVE; 342 j++; 343 344 p[j] = p[0]; 345 f[j] = f[0]; 346 j++; 347 } else { 348 p[j] = p[0]; 349 f[j] = f[0]; 350 j++; 351 } 352 353 prev_is_curve = TRUE; 354 } else { 355 p[j] = points[i]; 356 if (is_quadratic (flags[i])) 357 f[j] = QUADRATIC_OFF_CURVE; 358 else 359 f[j] = ON_CURVE; 360 j++; 361 362 p[j] = p[0]; 363 if (is_quadratic (flags[i])) 364 f[j] = QUADRATIC_OFF_CURVE; 365 else 366 f[j] = ON_CURVE; 367 j++; 368 } 369 370 set_double_curves (f, j); 371 *length = remove_hidden_points (p, f, j, 2 * len); 372 } 373 374 /** Get path data in .bf format. See BirdFontFile.get_point_data () for 375 * format specification. 376 */ 377 GString* get_bf_contour_data (guint unicode, FT_Vector* points, char* flags, int length, double units_per_em, int* err) { 378 GString* bf = g_string_new (""); 379 GString* contour; 380 int i = 0; 381 FT_Vector* new_points; 382 char* new_flags; 383 double x0, x1, x2; 384 double y0, y1, y2; 385 gchar coordinate_buffer[80]; 386 gchar* coordinate = (gchar*) &coordinate_buffer; 387 double units = get_units (units_per_em); 388 guint prev_is_curve; 389 int points_length; 390 391 if (length == 0) { 392 return bf; 393 } 394 395 points_length = length; 396 create_contour (unicode, points, flags, &length, &new_points, &new_flags, err); 397 398 x0 = new_points[0].x * units; 399 y0 = new_points[0].y * units; 400 401 g_string_printf (bf, "S "); 402 403 g_ascii_formatd (coordinate, 80, "%f", x0); 404 g_string_append (bf, coordinate); 405 g_string_append (bf, ","); 406 407 g_ascii_formatd (coordinate, 80, "%f", y0); 408 g_string_append (bf, coordinate); 409 410 i = 1; 411 while (i < length) { 412 contour = g_string_new (""); 413 414 if (is_hidden (new_flags[i])) { 415 // Skip it 416 g_string_append (contour, ""); 417 i++; 418 } else if (is_cubic (new_flags[i])) { 419 x0 = new_points[i].x * units; 420 y0 = new_points[i].y * units; 421 x1 = new_points[i+1].x * units; 422 y1 = new_points[i+1].y * units; 423 x2 = new_points[i+2].x * units; 424 y2 = new_points[i+2].y * units; 425 426 g_string_printf (contour, " C "); 427 428 g_ascii_formatd (coordinate, 80, "%f", x0); 429 g_string_append (contour, coordinate); 430 g_string_append (contour, ","); 431 432 g_ascii_formatd (coordinate, 80, "%f", y0); 433 g_string_append (contour, coordinate); 434 g_string_append (contour, " "); 435 436 g_ascii_formatd (coordinate, 80, "%f", x1); 437 g_string_append (contour, coordinate); 438 g_string_append (contour, ","); 439 440 g_ascii_formatd (coordinate, 80, "%f", y1); 441 g_string_append (contour, coordinate); 442 g_string_append (contour, " "); 443 444 g_ascii_formatd (coordinate, 80, "%f", x2); 445 g_string_append (contour, coordinate); 446 g_string_append (contour, ","); 447 448 g_ascii_formatd (coordinate, 80, "%f", y2); 449 g_string_append (contour, coordinate); 450 451 i += 3; 452 } else if (is_double_curve (new_flags[i])) { 453 x0 = new_points[i].x * units; 454 y0 = new_points[i].y * units; 455 x1 = new_points[i+1].x * units; 456 y1 = new_points[i+1].y * units; 457 x2 = new_points[i+2].x * units; 458 y2 = new_points[i+2].y * units; 459 460 g_string_printf (contour, " D "); 461 462 g_ascii_formatd (coordinate, 80, "%f", x0); 463 g_string_append (contour, coordinate); 464 g_string_append (contour, ","); 465 466 g_ascii_formatd (coordinate, 80, "%f", y0); 467 g_string_append (contour, coordinate); 468 g_string_append (contour, " "); 469 470 g_ascii_formatd (coordinate, 80, "%f", x1); 471 g_string_append (contour, coordinate); 472 g_string_append (contour, ","); 473 474 g_ascii_formatd (coordinate, 80, "%f", y1); 475 g_string_append (contour, coordinate); 476 g_string_append (contour, " "); 477 478 g_ascii_formatd (coordinate, 80, "%f", x2); 479 g_string_append (contour, coordinate); 480 g_string_append (contour, ","); 481 482 g_ascii_formatd (coordinate, 80, "%f", y2); 483 g_string_append (contour, coordinate); 484 485 i += 3; 486 } else if (is_quadratic (new_flags[i])) { 487 x0 = new_points[i].x * units; 488 y0 = new_points[i].y * units; 489 x1 = new_points[i+1].x * units; 490 y1 = new_points[i+1].y * units; 491 492 g_string_printf (contour, " Q "); 493 494 g_ascii_formatd (coordinate, 80, "%f", x0); 495 g_string_append (contour, coordinate); 496 g_string_append (contour, ","); 497 498 g_ascii_formatd (coordinate, 80, "%f", y0); 499 g_string_append (contour, coordinate); 500 g_string_append (contour, " "); 501 502 g_ascii_formatd (coordinate, 80, "%f", x1); 503 g_string_append (contour, coordinate); 504 g_string_append (contour, ","); 505 506 g_ascii_formatd (coordinate, 80, "%f", y1); 507 g_string_append (contour, coordinate); 508 509 i += 2; 510 } else if (is_line (new_flags[i])) { 511 x0 = new_points[i].x * units; 512 y0 = new_points[i].y * units; 513 514 g_string_printf (contour, " L "); 515 516 g_ascii_formatd (coordinate, 80, "%f", x0); 517 g_string_append (contour, coordinate); 518 g_string_append (contour, ","); 519 520 g_ascii_formatd (coordinate, 80, "%f", y0); 521 g_string_append (contour, coordinate); 522 523 i += 1; 524 } else { 525 contour = g_string_new (""); 526 g_warning ("WARNING Can't parse outline.\n"); 527 *err = 1; 528 i++; 529 } 530 531 g_string_append (bf, contour->str); 532 g_string_free (contour, TRUE); 533 } 534 535 free (new_points); 536 free (new_flags); 537 538 return bf; 539 } 540 541 /** Get path element in .bf format. */ 542 GString* get_bf_path (guint unicode, FT_Face face, double units_per_em, int* err) { 543 GString* bf = g_string_new (""); 544 GString* contour; 545 FT_Error error; 546 int i; 547 int start; 548 int end; 549 550 if (face->glyph->outline.n_points == 0) { 551 return bf; 552 } 553 554 start = 0; 555 for (i = 0; i < face->glyph->outline.n_contours; i++) { 556 end = face->glyph->outline.contours [i]; 557 contour = get_bf_contour_data (unicode, face->glyph->outline.points + start, face->glyph->outline.tags + start, end - start, units_per_em, err); 558 g_string_append_printf (bf, "\t\t<path data=\"%s\" />\n", contour->str); 559 g_string_free (contour, TRUE); 560 start = face->glyph->outline.contours [i] + 1; 561 } 562 563 return bf; 564 } 565 566 FontFace* open_font (const char* file) { 567 FT_Library library = NULL; 568 FT_Face face = NULL; 569 int error; 570 FontFace* font; 571 572 error = FT_Init_FreeType (&library); 573 if (error != OK) { 574 printf ("Freetype init error %d.\n", error); 575 return NULL; 576 } 577 578 error = FT_New_Face (library, file, 0, &face); 579 if (error) { 580 if (FT_Done_FreeType (library) != 0) { 581 g_warning ("Can't close freetype."); 582 } 583 584 g_warning ("Freetype font face error %d\n", error); 585 return NULL; 586 } 587 588 font = malloc (sizeof (FontFace)); 589 font->face = face; 590 font->library = library; 591 592 error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); 593 if (error) { 594 g_warning ("Freetype can not use Unicode, error: %d\n", error); 595 close_ft_font (font); 596 return NULL; 597 } 598 599 return font; 600 } 601 602 void close_ft_font (FontFace* font) { 603 if (font != NULL) { 604 if (font->face != NULL) { 605 if (FT_Done_Face (font->face) != 0) { 606 g_warning ("Can't close font."); 607 } 608 font->face = NULL; 609 } 610 611 if (font->library != NULL) { 612 if (FT_Done_FreeType (font->library) != 0) { 613 g_warning ("Can't close freetype."); 614 } 615 font->library = NULL; 616 } 617 618 free (font); 619 } 620 } 621 622 GString* load_glyph (FontFace* font, guint unicode) { 623 GString* glyph; 624 GString* paths; 625 int err = OK; 626 int gid; 627 FT_ULong charcode = (FT_ULong) unicode; 628 double units; 629 gchar line_position_buffer[80]; 630 gchar* line_position = (gchar*) &line_position_buffer; 631 FT_Error error; 632 633 if (font == NULL || font->face == NULL || font->library == NULL) { 634 printf ("WARNING No font in load_glyph"); 635 return NULL; 636 } 637 638 gid = FT_Get_Char_Index (font->face, charcode); 639 640 if (gid == 0) { 641 return NULL; 642 } 643 644 units = get_units (font->face->units_per_EM); 645 646 glyph = g_string_new ("<font>"); 647 648 g_string_append_printf (glyph, "<horizontal>\n"); 649 650 g_ascii_formatd (line_position, 80, "%f", font->face->ascender * units); 651 g_string_append_printf (glyph, "\t<top_limit>%s</top_limit>\n", line_position); 652 653 g_string_append_printf (glyph, "\t<base_line>0</base_line>\n"); 654 655 g_ascii_formatd (line_position, 80, "%f", font->face->descender * units); 656 g_string_append_printf (glyph, "\t<bottom_limit>%s</bottom_limit>\n", line_position); 657 658 g_string_append_printf (glyph, "</horizontal>\n"); 659 660 error = FT_Load_Glyph(font->face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 661 662 if (error != 0) { 663 printf ("WARNING Failed to load glyph."); 664 g_string_free (glyph, TRUE); 665 return NULL; 666 } 667 668 paths = get_bf_path (unicode, font->face, font->face->units_per_EM, &err); 669 670 if (err != OK) { 671 printf ("WARNING Can't load glyph."); 672 g_string_free (glyph, TRUE); 673 g_string_free (paths, TRUE); 674 return NULL; 675 } 676 677 g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode); 678 g_string_append_printf (glyph, "\t<selected id=\"0\" />\n"); 679 g_string_append_printf (glyph, "\t<glyph id=\"0\" left=\"%f\" right=\"%f\">\n", 680 0.0, font->face->glyph->metrics.horiAdvance * units); 681 682 g_string_append_printf (glyph, "%s", paths->str); 683 g_string_append_printf (glyph, "%s", "\t</glyph>"); 684 g_string_append_printf (glyph, "%s", "\t</collection>"); 685 g_string_append_printf (glyph, "%s", "</font>"); 686 687 g_string_free (paths, TRUE); 688 689 if (err != OK) { 690 g_warning ("Can't load glyph data."); 691 } 692 693 return glyph; 694 } 695 696 /** Get char code for a glyph. 697 * @gid glyph id 698 * @return character code 699 */ 700 FT_ULong get_charcode (FT_Face face, FT_UInt gid) { 701 FT_ULong charcode; 702 FT_UInt gindex; 703 704 // TODO: find the lookup function in freetype 705 706 charcode = FT_Get_First_Char (face, &gindex); 707 while (gindex != 0) { 708 charcode = FT_Get_Next_Char (face, charcode, &gindex); 709 if (gindex == gid) { 710 return charcode; 711 } 712 } 713 714 g_warning ("Can not find unicode value for gid %d.", gid); 715 return 0; 716 } 717 718 /** Height of letter. */ 719 int get_height (FT_Face face, guint unichar) { 720 int error; 721 FT_ULong charcode = (FT_ULong) unichar; 722 FT_UInt index = FT_Get_Char_Index (face, charcode); 723 724 error = FT_Load_Glyph (face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 725 if (error) { 726 g_warning ("Failed to obtain height. (%d)\n", error); 727 return 0; 728 } 729 730 return (int) face->glyph->metrics.height; 731 } 732 733 /** Height of lower case letters. */ 734 int get_xheight (FT_Face face) { 735 return get_height (face, 'x'); 736 } 737 738 /** Height of upper case letters. */ 739 int get_top (FT_Face face) { 740 return get_height (face, 'X'); 741 } 742 /** Obtain descender. */ 743 int get_descender (FT_Face face) { 744 int error; 745 FT_BBox bbox; 746 FT_ULong charcode = (FT_ULong) 'g'; 747 FT_UInt index = FT_Get_Char_Index (face, charcode); 748 FT_Glyph glyph; 749 750 error = FT_Load_Glyph (face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 751 if (error) { 752 g_warning ("Failed to obtain descender. (%d)\n", error); 753 return 0; 754 } 755 756 FT_Get_Glyph (face->glyph, &glyph); 757 FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_UNSCALED, &bbox); 758 759 return (int) bbox.yMin; 760 } 761 762 /** Append name table data to a gstring. */ 763 void append_description (GString* str, FT_SfntName* name_table_data) { 764 gchar* utf8_str; 765 gsize read, written; 766 GError* error = NULL; 767 768 if (name_table_data->encoding_id == 0) { // mac roman 769 utf8_str = g_convert (name_table_data->string, name_table_data->string_len, "utf-8", "macintosh", &read, &written, &error); 770 771 if (error == NULL) { 772 g_string_append (str, g_markup_escape_text (utf8_str, -1)); 773 g_free (utf8_str); 774 } else { 775 g_warning ("Error in append_description: %s\n", error->message); 776 g_error_free (error); 777 } 778 } else if (name_table_data->encoding_id == 1) { // windows unicode 779 utf8_str = g_convert (name_table_data->string, name_table_data->string_len, "utf-8", "ucs-2be", &read, &written, &error); 780 781 if (error == NULL) { 782 g_string_append (str, g_markup_escape_text (utf8_str, -1)); 783 g_free (utf8_str); 784 } else { 785 g_warning ("Error in append_description: %s\n", error->message); 786 g_error_free (error); 787 } 788 } else { 789 g_warning ("Encoding %u is not supported for platform %d.\n", name_table_data->encoding_id, name_table_data->platform_id); 790 } 791 } 792 793 int getIndexForNameIdEncoding (FT_Face face, int id, int encoding) { 794 FT_SfntName name_table_data; 795 FT_Error error; 796 int c = FT_Get_Sfnt_Name_Count (face); 797 int i = 0; 798 while (i < c){ 799 error = FT_Get_Sfnt_Name (face, i, &name_table_data); 800 801 if (error == 0 802 && name_table_data.name_id == id 803 && name_table_data.encoding_id == encoding) { 804 return i; 805 } 806 807 i++; 808 } 809 810 return -1; 811 } 812 813 int getIndexForNameId (FT_Face face, int id) { 814 int i = getIndexForNameIdEncoding (face, id, 1); 815 816 if (i != -1) { 817 return i; 818 819 } 820 821 return getIndexForNameIdEncoding (face, id, 0); 822 } 823 824 /** Convert font to bf format. 825 * @param face freetype font face 826 * @param err error code 827 * @return xml representation of a bf font 828 */ 829 GString* get_bf_font (FT_Face face, char* file, int* err) { 830 GString* bf = g_string_new (""); 831 GString* bf_data; 832 gchar* kerning; 833 GString* glyph; 834 FT_Error error; 835 FT_Long i; 836 FT_ULong charcode; 837 FT_UInt gid; 838 FT_SfntName name_table_data; 839 double units_per_em; 840 double units; 841 gchar line_position_buffer[80]; 842 gchar* line_position = (gchar*) &line_position_buffer; 843 int name_index; 844 845 *err = OK; 846 847 units_per_em = face->units_per_EM; 848 units = get_units (units_per_em); 849 850 if (face == NULL) { 851 return bf; 852 } 853 854 g_string_append (bf, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n"); 855 g_string_append (bf, "<font>\n"); 856 857 g_string_append_printf (bf, "<postscript_name>%s</postscript_name>\n", g_markup_escape_text (FT_Get_Postscript_Name(face), -1)); 858 g_string_append_printf (bf, "<name>%s</name>\n", g_markup_escape_text (face->family_name, -1)); 859 860 if (face->style_name != NULL) { 861 g_string_append_printf (bf, "<subfamily>%s</subfamily>\n", g_markup_escape_text (face->style_name, -1)); 862 } 863 864 // full name 865 name_index = getIndexForNameId (face, 4); 866 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 867 g_string_append (bf, "<full_name>"); 868 append_description (bf, &name_table_data); 869 g_string_append (bf, "</full_name>\n"); 870 } 871 872 // unique identifier 873 name_index = getIndexForNameId (face, 3); 874 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 875 g_string_append (bf, "<unique_identifier>"); 876 append_description (bf, &name_table_data); 877 g_string_append (bf, "</unique_identifier>\n"); 878 } 879 880 // version 881 name_index = getIndexForNameId (face, 5); 882 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 883 g_string_append (bf, "<version>"); 884 append_description (bf, &name_table_data); 885 g_string_append (bf, "</version>\n"); 886 } 887 888 name_index = getIndexForNameId (face, 10); 889 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { // description 890 g_string_append (bf, "<description>"); 891 append_description (bf, &name_table_data); 892 g_string_append (bf, "</description>\n"); 893 } 894 895 // copyright 896 name_index = getIndexForNameId (face, 0); 897 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 898 g_string_append (bf, "<copyright>"); 899 append_description (bf, &name_table_data); 900 g_string_append (bf, "</copyright>\n"); 901 } 902 903 name_index = getIndexForNameId (face, 7); 904 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 905 g_string_append (bf, "<trademark>"); 906 append_description (bf, &name_table_data); 907 g_string_append (bf, "</trademark>\n"); 908 } 909 910 name_index = getIndexForNameId (face, 8); 911 if (name_index != -1 && FT_Get_Sfnt_Name (face, 8, &name_table_data) == 0) { 912 g_string_append (bf, "<manefacturer>"); 913 append_description (bf, &name_table_data); 914 g_string_append (bf, "</manefacturer>\n"); 915 } 916 917 name_index = getIndexForNameId (face, 9); 918 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 919 g_string_append (bf, "<designer>"); 920 append_description (bf, &name_table_data); 921 g_string_append (bf, "</designer>\n"); 922 } 923 924 name_index = getIndexForNameId (face, 11); 925 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 926 g_string_append (bf, "<vendor_url>"); 927 append_description (bf, &name_table_data); 928 g_string_append (bf, "</vendor_url>\n"); 929 } 930 931 name_index = getIndexForNameId (face, 12); 932 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 933 g_string_append (bf, "<designer_url>"); 934 append_description (bf, &name_table_data); 935 g_string_append (bf, "</designer_url>\n"); 936 } 937 938 name_index = getIndexForNameId (face, 13); 939 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 940 g_string_append (bf, "<license>"); 941 append_description (bf, &name_table_data); 942 g_string_append (bf, "</license>\n"); 943 } 944 945 name_index = getIndexForNameId (face, 14); 946 if (name_index != -1 && FT_Get_Sfnt_Name (face, name_index, &name_table_data) == 0) { 947 g_string_append (bf, "<license_url>"); 948 append_description (bf, &name_table_data); 949 g_string_append (bf, "</license_url>\n"); 950 } 951 952 g_string_append_printf (bf, "<backup>%s</backup>\n", g_markup_escape_text (file, -1)); 953 954 955 g_string_append_printf (bf, "<horizontal>\n"); 956 957 g_ascii_formatd (line_position, 80, "%f", face->ascender * units); 958 g_string_append_printf (bf, "\t<top_limit>%s</top_limit>\n", line_position); 959 960 g_ascii_formatd (line_position, 80, "%f", get_top (face) * units); 961 g_string_append_printf (bf, "\t<top_position>%s</top_position>\n", line_position); 962 963 g_ascii_formatd (line_position, 80, "%f", get_xheight (face) * units); 964 g_string_append_printf (bf, "\t<x-height>%s</x-height>\n", line_position); 965 966 g_string_append_printf (bf, "\t<base_line>0</base_line>\n"); 967 968 g_ascii_formatd (line_position, 80, "%f", get_descender (face) * units); 969 g_string_append_printf (bf, "\t<bottom_position>%s</bottom_position>\n", line_position); 970 971 g_ascii_formatd (line_position, 80, "%f", face->descender * units); 972 g_string_append_printf (bf, "\t<bottom_limit>%s</bottom_limit>\n", line_position); 973 974 g_string_append_printf (bf, "</horizontal>\n"); 975 976 // space character 977 gid = FT_Get_Char_Index (face, ' '); 978 if (gid != 0) { 979 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 980 g_string_append_printf (bf, "<collection unicode=\"U+20\">\n"); 981 g_string_append_printf (bf, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 982 983 bf_data = get_bf_path (charcode, face, units_per_em, err); 984 g_string_append (bf, bf_data->str); 985 986 g_string_append (bf, "\t</glyph>\n"); 987 g_string_append_printf (bf, "</collection>\n"); 988 } 989 990 // glyph outlines 991 for (i = 0; i < face->num_glyphs; i++) { 992 error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 993 if (error) { 994 g_warning ("Freetype failed to load glyph %d.\n", (int)i); 995 g_warning ("FT_Load_Glyph error %d\n", error); 996 *err = 1; 997 return bf; 998 } 999 1000 if (face->glyph->format != ft_glyph_format_outline) { 1001 g_warning ("Freetype error no outline found in glyph.\n"); 1002 *err = 1; 1003 return bf; 1004 } 1005 1006 charcode = get_charcode (face, i); 1007 glyph = g_string_new (""); 1008 1009 if (charcode > 32) { // not control character 1010 g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode); 1011 g_string_append_printf (glyph, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 1012 1013 bf_data = get_bf_path (charcode, face, units_per_em, err); 1014 g_string_append (glyph, bf_data->str); 1015 1016 g_string_append (glyph, "\t</glyph>\n"); 1017 g_string_append_printf (glyph, "</collection>\n"); 1018 } else { 1019 g_warning ("Ignoring control character, %d.", (guint)charcode); 1020 } 1021 1022 g_string_append (bf, glyph->str); 1023 g_string_free (glyph, TRUE); 1024 } 1025 1026 bird_font_open_font_format_reader_append_kerning (bf, file); 1027 1028 g_string_append (bf, "</font>\n"); 1029 1030 return bf; 1031 } 1032 1033 /** Load typeface with freetype2 and return the result as a bf font. 1034 * Parameter err will be set to non zero vaule if an error occurs. 1035 */ 1036 GString* load_freetype_font (char* file, int* err) { 1037 GString* bf = NULL; 1038 1039 FT_Library library; 1040 FT_Face face; 1041 int error; 1042 FT_Glyph glyph; 1043 FT_UInt glyph_index; 1044 1045 error = FT_Init_FreeType (&library); 1046 if (error != OK) { 1047 g_warning ("Freetype init error %d.\n", error); 1048 *err = error; 1049 return bf; 1050 } 1051 1052 error = FT_New_Face (library, file, 0, &face); 1053 if (error) { 1054 g_warning ("Freetype font face error %d\n", error); 1055 *err = error; 1056 return bf; 1057 } 1058 1059 error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); 1060 if (error) { 1061 g_warning ("Freetype can not use Unicode, error: %d\n", error); 1062 *err = error; 1063 return bf; 1064 } 1065 1066 error = FT_Set_Char_Size (face, 0, 800, 300, 300); 1067 if (error) { 1068 g_warning ("Freetype FT_Set_Char_Size failed, error: %d.\n", error); 1069 *err = error; 1070 return bf; 1071 } 1072 1073 bf = get_bf_font (face, file, &error); 1074 if (error != OK) { 1075 g_warning ("Failed to parse font.\n"); 1076 *err = error; 1077 return bf; 1078 } 1079 1080 FT_Done_Face ( face ); 1081 FT_Done_FreeType( library ); 1082 1083 *err = OK; 1084 return bf; 1085 } 1086 1087