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.
Export open paths
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 && name_table_data->platform_id == 1) { // 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 && name_table_data->platform_id == 3) { // 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.\n", name_table_data->encoding_id); 790 } 791 } 792 793 /** Convert font to bf format. 794 * @param face freetype font face 795 * @param err error code 796 * @return xml representation of a bf font 797 */ 798 GString* get_bf_font (FT_Face face, char* file, int* err) { 799 GString* bf = g_string_new (""); 800 GString* bf_data; 801 gchar* kerning; 802 GString* glyph; 803 FT_Error error; 804 FT_Long i; 805 FT_ULong charcode; 806 FT_UInt gid; 807 FT_SfntName name_table_data; 808 double units_per_em; 809 double units; 810 gchar line_position_buffer[80]; 811 gchar* line_position = (gchar*) &line_position_buffer; 812 813 *err = OK; 814 815 units_per_em = face->units_per_EM; 816 units = get_units (units_per_em); 817 818 if (face == NULL) { 819 return bf; 820 } 821 822 g_string_append (bf, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n"); 823 g_string_append (bf, "<font>\n"); 824 825 g_string_append_printf (bf, "<postscript_name>%s</postscript_name>\n", g_markup_escape_text (FT_Get_Postscript_Name(face), -1)); 826 g_string_append_printf (bf, "<name>%s</name>\n", g_markup_escape_text (face->family_name, -1)); 827 828 if (face->style_name != NULL) { 829 g_string_append_printf (bf, "<subfamily>%s</subfamily>\n", g_markup_escape_text (face->style_name, -1)); 830 } 831 832 if (FT_Get_Sfnt_Name (face, 4, &name_table_data) == 0) { // full name 833 g_string_append (bf, "<full_name>"); 834 append_description (bf, &name_table_data); 835 g_string_append (bf, "</full_name>\n"); 836 } 837 838 if (FT_Get_Sfnt_Name (face, 3, &name_table_data) == 0) { // unique identifier 839 g_string_append (bf, "<unique_identifier>"); 840 append_description (bf, &name_table_data); 841 g_string_append (bf, "</unique_identifier>\n"); 842 } 843 844 if (FT_Get_Sfnt_Name (face, 5, &name_table_data) == 0) { // version 845 g_string_append (bf, "<version>"); 846 append_description (bf, &name_table_data); 847 g_string_append (bf, "</version>\n"); 848 } 849 850 // FIXME: for some fonts will this return entry 0 (copyright) 851 if (FT_Get_Sfnt_Name (face, 10, &name_table_data) == 0) { // description 852 g_string_append (bf, "<description>"); 853 append_description (bf, &name_table_data); 854 g_string_append (bf, "</description>\n"); 855 } 856 857 if (FT_Get_Sfnt_Name (face, 0, &name_table_data) == 0) { // copyright 858 g_string_append (bf, "<copyright>"); 859 append_description (bf, &name_table_data); 860 g_string_append (bf, "</copyright>\n"); 861 } 862 863 if (FT_Get_Sfnt_Name (face, 7, &name_table_data) == 0) { 864 g_string_append (bf, "<trademark>"); 865 append_description (bf, &name_table_data); 866 g_string_append (bf, "</trademark>\n"); 867 } 868 869 if (FT_Get_Sfnt_Name (face, 8, &name_table_data) == 0) { 870 g_string_append (bf, "<manefacturer>"); 871 append_description (bf, &name_table_data); 872 g_string_append (bf, "</manefacturer>\n"); 873 } 874 875 if (FT_Get_Sfnt_Name (face, 9, &name_table_data) == 0) { 876 g_string_append (bf, "<designer>"); 877 append_description (bf, &name_table_data); 878 g_string_append (bf, "</designer>\n"); 879 } 880 881 if (FT_Get_Sfnt_Name (face, 11, &name_table_data) == 0) { 882 g_string_append (bf, "<vendor_url>"); 883 append_description (bf, &name_table_data); 884 g_string_append (bf, "</vendor_url>\n"); 885 } 886 887 if (FT_Get_Sfnt_Name (face, 12, &name_table_data) == 0) { 888 g_string_append (bf, "<designer_url>"); 889 append_description (bf, &name_table_data); 890 g_string_append (bf, "</designer_url>\n"); 891 } 892 893 if (FT_Get_Sfnt_Name (face, 13, &name_table_data) == 0) { 894 g_string_append (bf, "<license>"); 895 append_description (bf, &name_table_data); 896 g_string_append (bf, "</license>\n"); 897 } 898 899 if (FT_Get_Sfnt_Name (face, 14, &name_table_data) == 0) { 900 g_string_append (bf, "<license_url>"); 901 append_description (bf, &name_table_data); 902 g_string_append (bf, "</license_url>\n"); 903 } 904 905 g_string_append_printf (bf, "<backup>%s</backup>\n", g_markup_escape_text (file, -1)); 906 907 908 g_string_append_printf (bf, "<horizontal>\n"); 909 910 g_ascii_formatd (line_position, 80, "%f", face->ascender * units); 911 g_string_append_printf (bf, "\t<top_limit>%s</top_limit>\n", line_position); 912 913 g_ascii_formatd (line_position, 80, "%f", get_top (face) * units); 914 g_string_append_printf (bf, "\t<top_position>%s</top_position>\n", line_position); 915 916 g_ascii_formatd (line_position, 80, "%f", get_xheight (face) * units); 917 g_string_append_printf (bf, "\t<x-height>%s</x-height>\n", line_position); 918 919 g_string_append_printf (bf, "\t<base_line>0</base_line>\n"); 920 921 g_ascii_formatd (line_position, 80, "%f", get_descender (face) * units); 922 g_string_append_printf (bf, "\t<bottom_position>%s</bottom_position>\n", line_position); 923 924 g_ascii_formatd (line_position, 80, "%f", face->descender * units); 925 g_string_append_printf (bf, "\t<bottom_limit>%s</bottom_limit>\n", line_position); 926 927 g_string_append_printf (bf, "</horizontal>\n"); 928 929 // space character 930 gid = FT_Get_Char_Index (face, ' '); 931 if (gid != 0) { 932 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 933 g_string_append_printf (bf, "<collection unicode=\"U+20\">\n"); 934 g_string_append_printf (bf, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 935 936 bf_data = get_bf_path (charcode, face, units_per_em, err); 937 g_string_append (bf, bf_data->str); 938 939 g_string_append (bf, "\t</glyph>\n"); 940 g_string_append_printf (bf, "</collection>\n"); 941 } 942 943 // glyph outlines 944 for (i = 0; i < face->num_glyphs; i++) { 945 error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT | FT_LOAD_NO_SCALE); 946 if (error) { 947 g_warning ("Freetype failed to load glyph %d.\n", (int)i); 948 g_warning ("FT_Load_Glyph error %d\n", error); 949 *err = 1; 950 return bf; 951 } 952 953 if (face->glyph->format != ft_glyph_format_outline) { 954 g_warning ("Freetype error no outline found in glyph.\n"); 955 *err = 1; 956 return bf; 957 } 958 959 charcode = get_charcode (face, i); 960 glyph = g_string_new (""); 961 962 if (charcode > 32) { // not control character 963 g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode); 964 g_string_append_printf (glyph, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units); 965 966 bf_data = get_bf_path (charcode, face, units_per_em, err); 967 g_string_append (glyph, bf_data->str); 968 969 g_string_append (glyph, "\t</glyph>\n"); 970 g_string_append_printf (glyph, "</collection>\n"); 971 } else { 972 g_warning ("Ignoring control character, %d.", (guint)charcode); 973 } 974 975 g_string_append (bf, glyph->str); 976 g_string_free (glyph, TRUE); 977 } 978 979 bird_font_open_font_format_reader_append_kerning (bf, file); 980 981 g_string_append (bf, "</font>\n"); 982 983 return bf; 984 } 985 986 /** Load typeface with freetype2 and return the result as a bf font. 987 * Parameter err will be set to non zero vaule if an error occurs. 988 */ 989 GString* load_freetype_font (char* file, int* err) { 990 GString* bf = NULL; 991 992 FT_Library library; 993 FT_Face face; 994 int error; 995 FT_Glyph glyph; 996 FT_UInt glyph_index; 997 998 error = FT_Init_FreeType (&library); 999 if (error != OK) { 1000 g_warning ("Freetype init error %d.\n", error); 1001 *err = error; 1002 return bf; 1003 } 1004 1005 error = FT_New_Face (library, file, 0, &face); 1006 if (error) { 1007 g_warning ("Freetype font face error %d\n", error); 1008 *err = error; 1009 return bf; 1010 } 1011 1012 error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); 1013 if (error) { 1014 g_warning ("Freetype can not use Unicode, error: %d\n", error); 1015 *err = error; 1016 return bf; 1017 } 1018 1019 error = FT_Set_Char_Size (face, 0, 800, 300, 300); 1020 if (error) { 1021 g_warning ("Freetype FT_Set_Char_Size failed, error: %d.\n", error); 1022 *err = error; 1023 return bf; 1024 } 1025 1026 bf = get_bf_font (face, file, &error); 1027 if (error != OK) { 1028 g_warning ("Failed to parse font.\n"); 1029 *err = error; 1030 return bf; 1031 } 1032 1033 FT_Done_Face ( face ); 1034 FT_Done_FreeType( library ); 1035 1036 *err = OK; 1037 return bf; 1038 } 1039 1040