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