revert jeff's last commit since it breaks the build
[moon.git] / src / fontmanager.cpp
blob782cc9543f116a56b35f1f36252c58cf9c582a2b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * fontmanager.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2009 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <glib/gstdio.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <ctype.h>
20 #include "fontmanager.h"
21 #include "zip/unzip.h"
22 #include "debug.h"
23 #include "utils.h"
24 #include "list.h"
26 #include <fontconfig/fontconfig.h>
27 #include <fontconfig/fcfreetype.h>
28 #include FT_TRUETYPE_TABLES_H
29 #include FT_OUTLINE_H
30 #include FT_SYSTEM_H
31 #include FT_GLYPH_H
35 // OpenType's OS/2 fsSelection Table:
37 // http://www.microsoft.com/typography/otspec/os2.htm#fss
39 enum fsSelection {
40 fsSelectionItalic = (1 << 0),
41 fsSelectionUnderscore = (1 << 1),
42 fsSelectionNegative = (1 << 2),
43 fsSelectionOutlined = (1 << 3),
44 fsSelectionStrikeout = (1 << 4),
45 fsSelectionBold = (1 << 5),
46 fsSelectionRegular = (1 << 6),
47 fsSelectionUseTypoMetrics = (1 << 7),
48 fsSelectionWWS = (1 << 8),
49 fsSelectionOblique = (1 << 9),
52 #define FONT_FACE_SIZE 41.0
54 #define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
55 #define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
56 #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
57 #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)
59 #define EMBOLDEN_STRENGTH 0.75
60 #define EMBOLDEN_STRENGTH_26_6 DOUBLE_TO_26_6 (EMBOLDEN_STRENGTH)
61 #define EMBOLDEN_STRENGTH_16_16 DOUBLE_TO_16_16 (EMBOLDEN_STRENGTH)
63 #define ITALIC_SLANT -17.5
64 #define ITALIC_SLANT_RADIANS (ITALIC_SLANT * M_PI / 180.0)
66 static const FT_Matrix italicize = {
67 DOUBLE_TO_16_16 (1.0), DOUBLE_TO_16_16 (tan (ITALIC_SLANT_RADIANS)),
68 DOUBLE_TO_16_16 (0.0), DOUBLE_TO_16_16 (1.0)
71 #define LOAD_FLAGS (FT_LOAD_NO_BITMAP | /*FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT |*/ FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_TARGET_NORMAL)
76 // Silverlight -> FontConfig enumeration conversion utilities
79 #ifndef FC_WEIGHT_EXTRABLACK
80 #define FC_WEIGHT_EXTRABLACK 215
81 #endif
82 #ifndef FC_WEIGHT_ULTRABLACK
83 #define FC_WEIGHT_ULTRABLACK FC_WEIGHT_EXTRABLACK
84 #endif
86 // Silverlight accepts negative values ]0,-475[ as bold and everything over 1023 as normal
87 #define FONT_LOWER_BOLD_LIMIT -475
88 #define FONT_UPPER_BOLD_LIMIT 1024
90 bool
91 FontWeightIsBold (FontWeights weight)
93 if (weight > FONT_LOWER_BOLD_LIMIT)
94 return weight < 0 || (weight >= FontWeightsSemiBold && weight < FONT_UPPER_BOLD_LIMIT);
96 return false;
99 static int
100 fc_weight (FontWeights weight)
102 if ((weight < 0) && (weight > FONT_LOWER_BOLD_LIMIT))
103 return FC_WEIGHT_BLACK;
104 else if (weight < (FontWeightsThin + FontWeightsLight) / 2)
105 return FC_WEIGHT_ULTRALIGHT;
106 else if (weight < (FontWeightsLight + FontWeightsNormal) / 2)
107 return FC_WEIGHT_LIGHT;
108 else if (weight < (FontWeightsNormal + FontWeightsMedium) / 2)
109 return FC_WEIGHT_NORMAL;
110 else if (weight < (FontWeightsMedium + FontWeightsSemiBold) / 2)
111 return FC_WEIGHT_MEDIUM;
112 else if (weight < (FontWeightsSemiBold + FontWeightsBold) / 2)
113 return FC_WEIGHT_SEMIBOLD;
114 else if (weight < (FontWeightsBold + FontWeightsExtraBold) / 2)
115 return FC_WEIGHT_BOLD;
116 else if (weight < (FontWeightsExtraBold + FontWeightsBlack) / 2)
117 return FC_WEIGHT_ULTRABOLD;
118 else if (weight < FONT_UPPER_BOLD_LIMIT)
119 return FC_WEIGHT_BLACK;
120 else
121 return FC_WEIGHT_NORMAL;
124 static int
125 fc_width (FontStretches stretch)
127 switch (stretch) {
128 case FontStretchesUltraCondensed:
129 return FC_WIDTH_ULTRACONDENSED;
130 case FontStretchesExtraCondensed:
131 return FC_WIDTH_EXTRACONDENSED;
132 case FontStretchesCondensed:
133 return FC_WIDTH_CONDENSED;
134 case FontStretchesSemiCondensed:
135 return FC_WIDTH_SEMICONDENSED;
136 case FontStretchesNormal:
137 return FC_WIDTH_NORMAL;
138 #if 0
139 case FontStretchesMedium:
140 return FC_WIDTH_NORMAL;
141 #endif
142 case FontStretchesSemiExpanded:
143 return FC_WIDTH_SEMIEXPANDED;
144 case FontStretchesExpanded:
145 return FC_WIDTH_EXPANDED;
146 case FontStretchesExtraExpanded:
147 return FC_WIDTH_EXTRAEXPANDED;
148 case FontStretchesUltraExpanded:
149 return FC_WIDTH_ULTRAEXPANDED;
150 default:
151 return FC_WIDTH_NORMAL;
155 static int
156 fc_slant (FontStyles style)
158 switch (style) {
159 case FontStylesNormal:
160 return FC_SLANT_ROMAN;
161 // technically Olbique does not exists in SL 1.0 or 2.0 (it's in WPF) but the parser allows it
162 case FontStylesOblique:
163 return FC_SLANT_OBLIQUE;
164 case FontStylesItalic:
165 // Silverlight defaults bad values to Italic
166 default:
167 return FC_SLANT_ITALIC;
173 // Font-style parser utils
176 #define Width (1 << 0)
177 #define Weight (1 << 1)
178 #define Slant (1 << 2)
180 struct FontStyleInfo {
181 char *family_name;
182 FontStretches width;
183 FontWeights weight;
184 FontStyles slant;
185 int set;
188 static struct {
189 const char *name;
190 size_t len;
191 int type;
192 int value;
193 } style_hints[] = {
194 // widths
195 { "Ultra-Condensed", 15, Width, FontStretchesUltraCondensed },
196 { "Extra-Condensed", 15, Width, FontStretchesExtraCondensed },
197 { "Semi-Condensed", 14, Width, FontStretchesSemiCondensed },
198 { "UltraCondensed", 14, Width, FontStretchesUltraCondensed },
199 { "ExtraCondensed", 14, Width, FontStretchesExtraCondensed },
200 { "SemiCondensed", 13, Width, FontStretchesSemiCondensed },
201 { "Condensed", 9, Width, FontStretchesCondensed },
202 { "Cond", 4, Width, FontStretchesCondensed },
203 { "Ultra-Expanded", 14, Width, FontStretchesUltraExpanded },
204 { "Extra-Expanded", 14, Width, FontStretchesExtraExpanded },
205 { "Semi-Expanded", 13, Width, FontStretchesSemiExpanded },
206 { "UltraExpanded", 13, Width, FontStretchesUltraExpanded },
207 { "ExtraExpanded", 13, Width, FontStretchesExtraExpanded },
208 { "SemiExpanded", 12, Width, FontStretchesSemiExpanded },
209 { "Expanded", 8, Width, FontStretchesExpanded },
211 // weights
212 { "Thin", 4, Weight, FontWeightsThin },
213 { "Ultra-Light", 11, Weight, FontWeightsExtraLight },
214 { "Extra-Light", 11, Weight, FontWeightsExtraLight },
215 { "UltraLight", 10, Weight, FontWeightsExtraLight },
216 { "ExtraLight", 10, Weight, FontWeightsExtraLight },
217 { "Light", 5, Weight, FontWeightsLight },
218 { "Book", 4, Weight, FontWeightsNormal },
219 { "Medium", 6, Weight, FontWeightsMedium },
220 { "Demi-Bold", 9, Weight, FontWeightsSemiBold },
221 { "Semi-Bold", 9, Weight, FontWeightsSemiBold },
222 { "DemiBold", 8, Weight, FontWeightsSemiBold },
223 { "SemiBold", 8, Weight, FontWeightsSemiBold },
224 { "Bold", 4, Weight, FontWeightsBold },
225 { "Extra-Bold", 10, Weight, FontWeightsExtraBold },
226 { "Ultra-Bold", 10, Weight, FontWeightsExtraBold },
227 { "ExtraBold", 9, Weight, FontWeightsExtraBold },
228 { "UltraBold", 9, Weight, FontWeightsExtraBold },
229 { "Black", 5, Weight, FontWeightsBlack },
230 { "Heavy", 5, Weight, FontWeightsBlack },
231 { "Extra-Black", 11, Weight, FontWeightsExtraBlack },
232 { "Ultra-Black", 11, Weight, FontWeightsExtraBlack },
233 { "ExtraBlack", 10, Weight, FontWeightsExtraBlack },
234 { "UltraBlack", 10, Weight, FontWeightsExtraBlack },
236 // slants
237 { "Oblique", 7, Slant, FontStylesOblique },
238 { "Italic", 6, Slant, FontStylesItalic },
239 { "Kursiv", 6, Slant, FontStylesItalic },
241 // changes nothing
242 { "Regular", 7, 0, 0 },
243 { "W3", 2, 0, 0 }, // as in Hiragino Mincho Pro W3
246 static void
247 style_info_parse (const char *style, FontStyleInfo *info, bool family)
249 register const char *inptr = style;
250 const char *first_hint = NULL;
251 const char *token;
252 guint tokens = 0;
253 size_t len;
254 guint i;
256 if (!style)
257 return;
259 while (*inptr) {
260 while (*inptr && isspace ((int) ((unsigned char) *inptr)))
261 inptr++;
263 if (*inptr == '\0')
264 break;
266 token = inptr;
267 while (*inptr && !isspace ((int) ((unsigned char) *inptr)))
268 inptr++;
270 tokens++;
272 if (family && tokens == 1) {
273 // if parsing the family_name, first token must not be interpreted as a style hint
274 continue;
277 len = (size_t) (inptr - token);
278 for (i = 0; i < G_N_ELEMENTS (style_hints); i++) {
279 if (style_hints[i].len == len && !strncmp (style_hints[i].name, token, len)) {
280 switch (style_hints[i].type) {
281 case Width:
282 info->width = (FontStretches) style_hints[i].value;
283 info->set |= Width;
284 break;
285 case Weight:
286 info->weight = (FontWeights) style_hints[i].value;
287 info->set |= Weight;
288 break;
289 case Slant:
290 info->slant = (FontStyles) style_hints[i].value;
291 info->set |= Slant;
292 break;
295 if (!first_hint)
296 first_hint = token;
297 break;
301 if (family && i == G_N_ELEMENTS (style_hints)) {
302 // if we come across an unknown style hint when
303 // parsing the family_name, assume that any previously
304 // found style hints were not actually style hints,
305 // but instead just part of the family name.
306 info->width = FontStretchesNormal;
307 info->weight = FontWeightsNormal;
308 info->slant = FontStylesNormal;
309 info->set = 0;
311 first_hint = NULL;
315 if (family) {
316 if (first_hint)
317 info->family_name = g_strndup (style, first_hint - style);
318 else
319 info->family_name = g_strdup (style);
321 g_strstrip (info->family_name);
325 #ifdef LOGGING
326 static const char *
327 style_info_to_string (FontStretches stretch, FontWeights weight, FontStyles style)
329 static char namebuf[256];
330 guint i = 0;
331 char *p;
333 namebuf[0] = '\0';
334 p = namebuf;
336 if (stretch != FontStretchesNormal) {
337 while (style_hints[i].type == Width) {
338 if (style_hints[i].value == stretch) {
339 p = g_stpcpy (p, style_hints[i].name);
340 break;
343 i++;
347 if (weight != FontWeightsNormal) {
348 while (style_hints[i].type != Weight)
349 i++;
351 while (style_hints[i].type == Weight) {
352 if (style_hints[i].value == weight) {
353 if (p != namebuf)
354 *p++ = ' ';
356 p = g_stpcpy (p, style_hints[i].name);
357 break;
360 i++;
364 if (style != FontStylesNormal) {
365 while (style_hints[i].type != Slant)
366 i++;
368 while (i < G_N_ELEMENTS (style_hints)) {
369 if (style_hints[i].value == style) {
370 if (p != namebuf)
371 *p++ = ' ';
373 p = g_stpcpy (p, style_hints[i].name);
374 break;
377 i++;
381 return namebuf;
383 #endif
385 static bool
386 is_odttf (const char *name)
388 size_t len = strlen (name);
390 if (len > 6 && !g_ascii_strcasecmp (name + len - 6, ".odttf"))
391 return true;
393 return false;
398 // FontStream
401 struct FontStream {
402 bool obfuscated;
403 char guid[16];
404 FILE *fp;
407 static const char *
408 path_get_basename (const char *path)
410 const char *name;
412 if (!(name = strrchr (path, '/')))
413 return path;
415 return name + 1;
418 static bool
419 decode_guid (const char *in, char *guid)
421 const char *inptr = in;
422 int i = 16;
424 while (i > 0 && *inptr && *inptr != '.') {
425 if (*inptr == '-')
426 inptr++;
428 i--;
430 if (*inptr >= '0' && *inptr <= '9')
431 guid[i] = (*inptr - '0') * 16;
432 else if (*inptr >= 'a' && *inptr <= 'f')
433 guid[i] = ((*inptr - 'a') + 10) * 16;
434 else if (*inptr >= 'A' && *inptr <= 'F')
435 guid[i] = ((*inptr - 'A') + 10) * 16;
436 else
437 return false;
439 inptr++;
441 if (*inptr >= '0' && *inptr <= '9')
442 guid[i] += (*inptr - '0');
443 else if (*inptr >= 'a' && *inptr <= 'f')
444 guid[i] += ((*inptr - 'a') + 10);
445 else if (*inptr >= 'A' && *inptr <= 'F')
446 guid[i] += ((*inptr - 'A') + 10);
447 else
448 return false;
450 inptr++;
453 if (i > 0)
454 return false;
456 return true;
459 static bool
460 font_stream_set_guid (FT_Stream stream, const char *guid)
462 FontStream *fs = (FontStream *) stream->descriptor.pointer;
464 if (guid) {
465 fs->obfuscated = decode_guid (guid, fs->guid);
466 } else {
467 fs->obfuscated = false;
470 return fs->obfuscated;
473 static unsigned long
474 font_stream_read (FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count)
476 FontStream *fs = (FontStream *) stream->descriptor.pointer;
477 size_t nread;
479 if (fseek (fs->fp, (long) offset, SEEK_SET) == -1)
480 return 0;
482 if (count == 0 || buffer == NULL)
483 return 0;
485 nread = fread (buffer, 1, count, fs->fp);
487 if (fs->obfuscated && offset < 32 && nread > 0) {
488 /* obfuscated font... need to deobfuscate */
489 unsigned long i = offset;
490 unsigned long j = 0;
492 for ( ; i < 32 && j < nread; i++, j++)
493 buffer[j] = buffer[j] ^ fs->guid[i % 16];
496 return nread;
499 static void
500 font_stream_reset (FT_Stream stream)
502 FontStream *fs = (FontStream *) stream->descriptor.pointer;
504 rewind (fs->fp);
505 stream->pos = 0;
508 static void
509 font_stream_close (FT_Stream stream)
511 // no-op
514 static FT_Stream
515 font_stream_new (const char *filename, const char *guid)
517 FT_Stream stream;
518 FontStream *fs;
519 FILE *fp;
521 if (!(fp = fopen (filename, "r")))
522 return NULL;
524 fs = (FontStream *) g_malloc (sizeof (FontStream));
525 fs->obfuscated = false;
526 fs->fp = fp;
528 stream = (FT_Stream) g_malloc0 (sizeof (FT_StreamRec));
529 stream->close = font_stream_close;
530 stream->read = font_stream_read;
531 stream->descriptor.pointer = fs;
533 fseek (fp, 0, SEEK_END);
534 stream->size = ftell (fp);
535 fseek (fp, 0, SEEK_SET);
537 font_stream_set_guid (stream, guid);
539 return stream;
542 static void
543 font_stream_destroy (FT_Stream stream)
545 FontStream *fs = (FontStream *) stream->descriptor.pointer;
547 g_free (stream);
548 fclose (fs->fp);
549 g_free (fs);
554 // FaceInfo
557 struct FontFile : public List::Node {
558 GPtrArray *faces;
559 char *path, *guid;
561 FontFile (const char *path, const char *guid);
562 ~FontFile ();
565 struct FaceInfo {
566 FontStyleInfo style;
567 char *family_name;
568 FontFile *file;
569 int index;
571 FaceInfo (FontFile *file, FT_Face face, int index);
572 ~FaceInfo ();
575 FaceInfo::FaceInfo (FontFile *file, FT_Face face, int index)
577 LOG_FONT (stderr, " * indexing %s[%d]: family=\"%s\"; style=\"%s\"\n",
578 path_get_basename (file->path), index, face->family_name, face->style_name);
580 style.width = FontStretchesNormal;
581 style.weight = FontWeightsNormal;
582 style.slant = FontStylesNormal;
583 style.family_name = NULL;
584 style.set = 0;
586 // extract whatever little style info we can from the family name
587 style_info_parse (face->family_name, &style, true);
589 // style info parsed from style_name overrides anything we got from family_name
590 style_info_parse (face->style_name, &style, false);
592 family_name = style.family_name;
594 LOG_FONT (stderr, " * indexed as %s; %s\n", family_name,
595 style_info_to_string (style.width, style.weight, style.slant));
597 this->index = index;
598 this->file = file;
601 FaceInfo::~FaceInfo ()
603 g_free (family_name);
608 // FontFile
611 FontFile::FontFile (const char *path, const char *guid)
613 this->path = g_strdup (path);
614 this->guid = g_strdup (guid);
615 faces = NULL;
618 FontFile::~FontFile ()
620 if (faces != NULL) {
621 FaceInfo *face;
623 for (guint i = 0; i < faces->len; i++) {
624 face = (FaceInfo *) faces->pdata[i];
625 delete face;
628 g_ptr_array_free (faces, true);
631 g_free (path);
632 g_free (guid);
637 // FontIndex
640 struct FontIndex {
641 List *fonts;
642 char *name;
643 char *path;
645 FontIndex (const char *name);
646 ~FontIndex ();
648 void CacheFontInfo (FT_Library libft2, const char *filename, FT_Stream stream, FT_Face face, const char *guid);
651 FontIndex::FontIndex (const char *name)
653 this->name = g_strdup (name);
654 fonts = new List ();
655 path = NULL;
658 FontIndex::~FontIndex ()
660 g_free (name);
661 g_free (path);
662 delete fonts;
665 void
666 FontIndex::CacheFontInfo (FT_Library libft2, const char *filename, FT_Stream stream, FT_Face face, const char *guid)
668 int i = 0, nfaces = face->num_faces;
669 FT_Open_Args args;
670 FontFile *file;
671 FaceInfo *fi;
673 LOG_FONT (stderr, " * caching font info for `%s'...\n", filename);
675 file = new FontFile (filename, guid);
676 file->faces = g_ptr_array_new ();
678 do {
679 args.flags = FT_OPEN_STREAM;
680 args.stream = stream;
682 if (i > 0 && FT_Open_Face (libft2, &args, i, &face) != 0)
683 break;
685 fi = new FaceInfo (file, face, i);
686 g_ptr_array_add (file->faces, fi);
688 FT_Done_Face (face);
690 font_stream_reset (stream);
692 i++;
693 } while (i < nfaces);
695 fonts->Append (file);
698 static void
699 font_index_destroy (gpointer user_data)
701 delete ((FontIndex *) user_data);
706 // FontFace
709 FontFace::FontFace (FontManager *manager, FT_Face face, char *key)
711 FT_Set_Pixel_Sizes (face, 0, (int) FONT_FACE_SIZE);
712 this->cur_size = FONT_FACE_SIZE;
713 this->manager = manager;
714 this->ref_count = 1;
715 this->face = face;
716 this->key = key;
718 g_hash_table_insert (manager->faces, key, this);
721 FontFace::~FontFace ()
723 FT_Stream stream;
725 g_hash_table_steal (manager->faces, key);
727 stream = face->stream;
728 FT_Done_Face (face);
729 font_stream_destroy (stream);
730 g_free (key);
733 void
734 FontFace::ref ()
736 ref_count++;
739 void
740 FontFace::unref ()
742 ref_count--;
744 if (ref_count == 0)
745 delete this;
748 const char *
749 FontFace::GetFamilyName ()
751 return face->family_name;
754 const char *
755 FontFace::GetStyleName ()
757 return face->style_name;
760 bool
761 FontFace::IsScalable ()
763 return FT_IS_SCALABLE (face);
766 bool
767 FontFace::IsItalic ()
769 return (face->style_flags & FT_STYLE_FLAG_ITALIC);
772 bool
773 FontFace::IsBold ()
775 return (face->style_flags & FT_STYLE_FLAG_BOLD);
778 gunichar
779 FontFace::GetCharFromIndex (guint32 index)
781 gunichar unichar;
782 guint32 idx;
784 if (index == 0)
785 return 0;
787 unichar = FT_Get_First_Char (face, &idx);
788 while (idx != index && idx != 0)
789 unichar = FT_Get_Next_Char (face, unichar, &idx);
791 if (idx == 0)
792 unichar = 0;
794 return unichar;
797 guint32
798 FontFace::GetCharIndex (gunichar unichar)
800 return FcFreeTypeCharIndex (face, unichar);
803 bool
804 FontFace::HasChar (gunichar unichar)
806 return FcFreeTypeCharIndex (face, unichar) != 0;
809 void
810 FontFace::GetExtents (double size, bool gapless, FontFaceExtents *extents)
812 double scale = size / face->units_per_EM;
814 if (FT_IS_SFNT (face)) {
815 TT_HoriHeader *hhea = (TT_HoriHeader *) FT_Get_Sfnt_Table (face, ft_sfnt_hhea);
816 TT_OS2 *os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2);
817 int height, ascender, descender;
819 if (os2 && (os2->fsSelection & fsSelectionUseTypoMetrics)) {
820 // Use the typographic Ascender, Descender, and LineGap values for everything.
821 height = os2->sTypoAscender - os2->sTypoDescender;
822 if (!gapless)
823 height += os2->sTypoLineGap;
825 descender = -os2->sTypoDescender;
826 ascender = os2->sTypoAscender;
827 } else {
828 // Calculate the LineSpacing for both the hhea table and the OS/2 table.
829 int hhea_height = hhea->Ascender + abs (hhea->Descender) + hhea->Line_Gap;
830 int os2_height = os2 ? (os2->usWinAscent + os2->usWinDescent) : 0;
832 // The LineSpacing is the maximum of the two sumations.
833 height = MAX (hhea_height, os2_height);
835 if (gapless && os2) {
836 // Subtract the OS/2 typographic LineGap (not the hhea LineGap)
837 height -= os2->sTypoLineGap;
840 // If the OS/2 table exists, use usWinAscent as the
841 // ascender. Otherwise use hhea's Ascender value.
842 ascender = os2 ? os2->usWinAscent : hhea->Ascender;
844 // The Descender becomes the difference between the
845 // LineSpacing and the Ascender.
846 descender = height - ascender;
849 extents->descent = -descender * scale;
850 extents->ascent = ascender * scale;
851 extents->height = height * scale;
852 } else {
853 // Fall back to the default FreeType2 values.
854 extents->descent = face->descender * scale;
855 extents->ascent = face->ascender * scale;
856 extents->height = face->height * scale;
859 extents->underline_thickness = face->underline_thickness * scale;
860 extents->underline_position = -face->underline_position * scale;
861 extents->underline_position += ((extents->underline_thickness + 1) / 2.0);
863 if (extents->underline_thickness < 1.0)
864 extents->underline_thickness = 1.0;
867 double
868 FontFace::Kerning (double size, guint32 left, guint32 right)
870 FT_Vector kerning;
872 if (!FT_HAS_KERNING (face) || left == 0 || right == 0)
873 return 0.0;
875 if (size <= FONT_FACE_SIZE) {
876 if (cur_size != FONT_FACE_SIZE) {
877 FT_Set_Pixel_Sizes (face, 0, (int) FONT_FACE_SIZE);
878 cur_size = FONT_FACE_SIZE;
881 FT_Get_Kerning (face, left, right, FT_KERNING_DEFAULT, &kerning);
883 return (kerning.x * size) / (FONT_FACE_SIZE * 64.0);
884 } else {
885 if (cur_size != size) {
886 FT_Set_Pixel_Sizes (face, 0, (int) size);
887 cur_size = size;
890 FT_Get_Kerning (face, left, right, FT_KERNING_DEFAULT, &kerning);
892 return kerning.x / 64.0;
896 static int
897 font_move_to (FT_Vector *to, void *user_data)
899 moon_path *path = (moon_path *) user_data;
900 double x, y;
902 x = DOUBLE_FROM_26_6 (to->x);
903 y = DOUBLE_FROM_26_6 (to->y);
905 moon_move_to (path, x, y);
907 return 0;
910 static int
911 font_line_to (FT_Vector *to, void *user_data)
913 moon_path *path = (moon_path *) user_data;
914 double x, y;
916 x = DOUBLE_FROM_26_6 (to->x);
917 y = DOUBLE_FROM_26_6 (to->y);
919 moon_line_to (path, x, y);
921 return 0;
924 static int
925 font_conic_to (FT_Vector *control, FT_Vector *to, void *user_data)
927 moon_path *path = (moon_path *) user_data;
928 double x3, y3;
929 double x, y;
931 x = DOUBLE_FROM_26_6 (control->x);
932 y = DOUBLE_FROM_26_6 (control->y);
934 x3 = DOUBLE_FROM_26_6 (to->x);
935 y3 = DOUBLE_FROM_26_6 (to->y);
937 moon_quad_curve_to (path, x, y, x3, y3);
939 return 0;
942 static int
943 font_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user_data)
945 moon_path *path = (moon_path *) user_data;
946 double x0, y0;
947 double x1, y1;
948 double x2, y2;
950 x0 = DOUBLE_FROM_26_6 (control1->x);
951 y0 = DOUBLE_FROM_26_6 (control1->y);
953 x1 = DOUBLE_FROM_26_6 (control2->x);
954 y1 = DOUBLE_FROM_26_6 (control2->y);
956 x2 = DOUBLE_FROM_26_6 (to->x);
957 y2 = DOUBLE_FROM_26_6 (to->y);
959 moon_curve_to (path, x0, y0, x1, y1, x2, y2);
961 return 0;
964 static const FT_Outline_Funcs outline_funcs = {
965 (FT_Outline_MoveToFunc) font_move_to,
966 (FT_Outline_LineToFunc) font_line_to,
967 (FT_Outline_ConicToFunc) font_conic_to,
968 (FT_Outline_CubicToFunc) font_cubic_to,
969 0, /* shift */
970 0, /* delta */
973 bool
974 FontFace::LoadGlyph (double size, GlyphInfo *glyph, StyleSimulations simulate)
976 FT_Glyph_Metrics *metrics;
977 FT_Fixed hori_adj = 0;
978 FT_Pos bbox_adj = 0;
979 FT_Matrix matrix;
980 double scale;
982 if (!face)
983 return false;
985 if (size <= FONT_FACE_SIZE) {
986 if (cur_size != FONT_FACE_SIZE) {
987 FT_Set_Pixel_Sizes (face, 0, (int) FONT_FACE_SIZE);
988 cur_size = FONT_FACE_SIZE;
991 scale = size / FONT_FACE_SIZE;
992 } else {
993 if (cur_size != size) {
994 FT_Set_Pixel_Sizes (face, 0, (int) size);
995 cur_size = size;
998 scale = 1.0;
1001 if (FT_Load_Glyph (face, glyph->index, LOAD_FLAGS) != 0)
1002 return false;
1004 if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL) != 0)
1005 return false;
1007 // invert the glyph over the y-axis and scale
1008 matrix.xx = DOUBLE_TO_16_16 (scale);
1009 matrix.xy = 0;
1010 matrix.yx = 0;
1011 matrix.yy = -DOUBLE_TO_16_16 (scale);
1013 if ((simulate & StyleSimulationsBold) != 0) {
1014 FT_Outline_Embolden (&face->glyph->outline, EMBOLDEN_STRENGTH_26_6);
1015 hori_adj = EMBOLDEN_STRENGTH_16_16;
1016 bbox_adj = EMBOLDEN_STRENGTH_26_6;
1019 if ((simulate & StyleSimulationsItalic) != 0)
1020 FT_Matrix_Multiply (&italicize, &matrix);
1022 glyph->path = moon_path_new (8);
1023 FT_Outline_Transform (&face->glyph->outline, &matrix);
1024 FT_Outline_Decompose (&face->glyph->outline, &outline_funcs, glyph->path);
1026 metrics = &face->glyph->metrics;
1028 glyph->metrics.horiBearingX = DOUBLE_FROM_26_6 (metrics->horiBearingX) * scale;
1029 //glyph->metrics.horiBearingY = DOUBLE_FROM_26_6 (metrics->horiBearingY) * scale;
1030 // always prefer linearHoriAdvance over horiAdvance since the later is rounded to an integer
1031 glyph->metrics.horiAdvance = DOUBLE_FROM_16_16 (face->glyph->linearHoriAdvance + hori_adj) * scale;
1032 //glyph->metrics.height = DOUBLE_FROM_26_6 (metrics->height + bbox_adj) * scale;
1033 //glyph->metrics.width = DOUBLE_FROM_26_6 (metrics->width + bbox_adj) * scale;
1035 return true;
1038 static void
1039 font_face_destroy (gpointer data)
1041 FontFace *face = (FontFace *) data;
1043 delete face;
1048 // FontManager
1051 FontManager::FontManager ()
1053 FcPattern *pattern;
1055 resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, font_index_destroy);
1056 faces = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, font_face_destroy);
1057 system_faces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1059 FT_Init_FreeType (&libft2);
1061 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "Sans",
1062 FC_SIZE, FcTypeDouble, 10.0, NULL);
1064 if (FcPatternGetDouble (pattern, FC_DPI, 0, &dpi) != FcResultMatch)
1065 dpi = 72.0;
1067 FcPatternDestroy (pattern);
1068 root = NULL;
1071 FontManager::~FontManager ()
1073 g_hash_table_destroy (system_faces);
1074 g_hash_table_destroy (resources);
1075 g_hash_table_destroy (faces);
1076 FT_Done_FreeType (libft2);
1078 if (root) {
1079 RemoveDir (root);
1080 g_free (root);
1084 static bool
1085 IndexFontSubdirectory (FT_Library libft2, const char *name, GString *path, FontIndex **out)
1087 FontIndex *fontdir = *out;
1088 const gchar *dirname;
1089 FT_Open_Args args;
1090 FT_Stream stream;
1091 bool obfuscated;
1092 struct stat st;
1093 FT_Face face;
1094 size_t len;
1095 GDir *dir;
1097 if (!(dir = g_dir_open (path->str, 0, NULL)))
1098 return fontdir != NULL;
1100 LOG_FONT (stderr, " * indexing font directory `%s'...\n", path->str);
1102 g_string_append_c (path, G_DIR_SEPARATOR);
1103 len = path->len;
1105 while ((dirname = g_dir_read_name (dir))) {
1106 if (!strcmp (dirname, "..") ||
1107 !strcmp (dirname, "."))
1108 continue;
1110 g_string_append (path, dirname);
1112 if (g_stat (path->str, &st) == -1)
1113 goto next;
1115 if (S_ISDIR (st.st_mode)) {
1116 IndexFontSubdirectory (libft2, name, path, &fontdir);
1117 goto next;
1120 if (!(stream = font_stream_new (path->str, NULL)))
1121 goto next;
1123 args.flags = FT_OPEN_STREAM;
1124 args.stream = stream;
1126 obfuscated = false;
1128 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1129 // not a valid font file... is it maybe an obfuscated font?
1130 if (!is_odttf (dirname) || !font_stream_set_guid (stream, dirname)) {
1131 font_stream_destroy (stream);
1132 goto next;
1135 font_stream_reset (stream);
1137 args.flags = FT_OPEN_STREAM;
1138 args.stream = stream;
1140 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1141 font_stream_destroy (stream);
1142 goto next;
1145 obfuscated = true;
1148 if (fontdir == NULL)
1149 fontdir = new FontIndex (name);
1151 // cache font info
1152 fontdir->CacheFontInfo (libft2, path->str, stream, face, obfuscated ? dirname : NULL);
1154 font_stream_destroy (stream);
1156 next:
1157 g_string_truncate (path, len);
1160 g_dir_close (dir);
1162 *out = fontdir;
1164 return fontdir != NULL;
1167 static FontIndex *
1168 IndexFontDirectory (FT_Library libft2, const char *name, const char *dirname)
1170 FontIndex *fontdir = NULL;
1171 GString *path;
1172 size_t len;
1174 path = g_string_new (dirname);
1175 len = path->len;
1177 if (!IndexFontSubdirectory (libft2, name, path, &fontdir)) {
1178 g_string_free (path, true);
1179 return NULL;
1182 g_string_truncate (path, len);
1183 fontdir->path = path->str;
1185 g_string_free (path, false);
1187 return fontdir;
1190 static FontIndex *
1191 IndexFontFile (FT_Library libft2, const char *name, const char *path)
1193 const char *filename = path_get_basename (name);
1194 FontIndex *index = NULL;
1195 FT_Open_Args args;
1196 FT_Stream stream;
1197 bool obfuscated;
1198 FT_Face face;
1200 LOG_FONT (stderr, " * indexing font file `%s'...\n", path);
1202 if (!(stream = font_stream_new (path, NULL)))
1203 return NULL;
1205 args.flags = FT_OPEN_STREAM;
1206 args.stream = stream;
1208 obfuscated = false;
1210 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1211 // not a valid font file... is it maybe an obfuscated font?
1212 if (!is_odttf (filename) || !font_stream_set_guid (stream, filename)) {
1213 font_stream_destroy (stream);
1214 return NULL;
1217 font_stream_reset (stream);
1219 args.flags = FT_OPEN_STREAM;
1220 args.stream = stream;
1222 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1223 font_stream_destroy (stream);
1224 return NULL;
1227 obfuscated = true;
1230 index = new FontIndex (name);
1231 index->path = g_strdup (path);
1233 // cache font info
1234 index->CacheFontInfo (libft2, path, stream, face, obfuscated ? filename : NULL);
1236 font_stream_destroy (stream);
1238 return index;
1241 void
1242 FontManager::AddResource (const char *resource, const char *path)
1244 FontIndex *index;
1245 struct stat st;
1247 LOG_FONT (stderr, "Adding font resource '%s' at %s\n", resource, path);
1249 if ((index = (FontIndex *) g_hash_table_lookup (resources, resource)))
1250 return;
1252 if (stat (path, &st) == -1)
1253 return;
1255 if (S_ISDIR (st.st_mode))
1256 index = IndexFontDirectory (libft2, resource, path);
1257 else if (S_ISREG (st.st_mode))
1258 index = IndexFontFile (libft2, resource, path);
1259 else
1260 return;
1262 if (index)
1263 g_hash_table_insert (resources, index->name, index);
1266 char *
1267 FontManager::AddResource (ManagedStreamCallbacks *stream)
1269 char buf[4096], *resource, *dirname, *path;
1270 unzFile zipfile;
1271 int nread, fd;
1272 gint64 pos;
1274 if (!stream->CanRead (stream->handle))
1275 return NULL;
1277 if (!root && !(root = CreateTempDir ("moonlight-fonts")))
1278 return NULL;
1280 // check if we've already added this resource
1281 resource = g_strdup_printf ("font-source://%p", stream->handle);
1282 if (g_hash_table_lookup (resources, resource) != NULL)
1283 return resource;
1285 snprintf (buf, sizeof (buf), "%p", stream->handle);
1286 path = g_build_filename (root, buf, NULL);
1288 if ((fd = g_open (path, O_CREAT | O_EXCL | O_WRONLY, 0600)) == -1) {
1289 g_free (resource);
1290 g_free (path);
1291 return NULL;
1294 // write the managed stream to disk
1295 pos = stream->Position (stream->handle);
1297 if (stream->CanSeek (stream->handle))
1298 stream->Seek (stream->handle, 0, SEEK_SET);
1300 while ((nread = stream->Read (stream->handle, buf, 0, sizeof (buf))) > 0) {
1301 if (write_all (fd, buf, (size_t) nread) == -1) {
1302 g_free (resource);
1303 close (fd);
1304 g_unlink (path);
1305 g_free (path);
1306 return NULL;
1310 // reset the stream to the original state
1311 if (stream->CanSeek (stream->handle) && pos != -1)
1312 stream->Seek (stream->handle, pos, SEEK_SET);
1314 close (fd);
1316 // check to see if the resource is zipped
1317 if ((zipfile = unzOpen (path))) {
1318 snprintf (buf, sizeof (buf), "%p.zip", stream->handle);
1319 dirname = g_build_filename (root, buf, NULL);
1321 // create a directory to contain our unzipped content
1322 if (g_mkdir (dirname, 0700) == -1) {
1323 unzClose (zipfile);
1324 g_free (resource);
1325 g_free (dirname);
1326 g_unlink (path);
1327 g_free (path);
1328 return NULL;
1331 // unzip the contents
1332 if (!ExtractAll (zipfile, dirname, CanonModeNone)) {
1333 RemoveDir (dirname);
1334 unzClose (zipfile);
1335 g_free (resource);
1336 g_free (dirname);
1337 g_unlink (path);
1338 g_free (path);
1339 return NULL;
1342 unzClose (zipfile);
1343 g_unlink (path);
1344 g_free (path);
1346 path = dirname;
1349 AddResource (resource, path);
1351 g_free (path);
1353 return resource;
1356 static int
1357 style_diff (FontStyleInfo *actual, FontStyleInfo *desired)
1359 #if 0
1360 // we convert to FontConfig for 2 reasons:
1361 // 1. negative values and values > 1023
1362 // 2. smaller ranges
1363 int weight = abs (fc_weight (actual->weight) - fc_weight (desired->weight));
1365 if (actual->slant == desired->slant)
1366 return weight;
1368 if (actual->slant == FontStylesNormal) {
1369 // we can emulate italic/oblique, but we would still prefer the real
1370 // italic font if we can find it so apply a slight penalty
1371 return 1000 + weight;
1374 // ouch, apply a huge penalty
1375 return 1000000 + weight;
1376 #else
1377 // convert to FontConfig values so that each style property fits within 8 bits
1378 int weight = abs (fc_weight (actual->weight) - fc_weight (desired->weight));
1379 int width = abs (fc_width (actual->width) - fc_width (desired->width));
1380 int slant = abs (fc_slant (actual->slant) - fc_slant (desired->slant));
1382 // weight has the highest priority, followed by weight and then slant
1383 return ((width & 0xff) << 16) | ((weight & 0xff) << 8) | (slant & 0xff);
1384 #endif
1387 static void
1388 canon_font_family_and_style (FontStyleInfo *desired, const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1390 desired->width = FontStretchesNormal;
1391 desired->weight = FontWeightsNormal;
1392 desired->slant = FontStylesNormal;
1393 desired->family_name = NULL;
1394 desired->set = 0;
1396 // extract whatever little style info we can from the family name
1397 style_info_parse (family, desired, true);
1399 // override style with user-specified attributes
1400 if (!(desired->set & Width))
1401 desired->width = stretch;
1402 if (!(desired->set & Weight))
1403 desired->weight = weight;
1404 if (!(desired->set & Slant))
1405 desired->slant = style;
1408 static FaceInfo *
1409 IndexMatchFace (FontIndex *index, const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1411 FontFile *file = (FontFile *) index->fonts->First ();
1412 FaceInfo *face, *best = NULL;
1413 FontStyleInfo desired;
1414 int closest = G_MAXINT;
1415 int diff;
1416 guint i;
1418 LOG_FONT (stderr, " * searching index for %s; %s\n", family, style_info_to_string (stretch, weight, style));
1420 canon_font_family_and_style (&desired, family, stretch, weight, style);
1422 LOG_FONT (stderr, " * canonicalized family/style: %s; %s\n", desired.family_name,
1423 style_info_to_string (desired.width, desired.weight, desired.slant));
1425 while (file != NULL) {
1426 for (i = 0; i < file->faces->len; i++) {
1427 face = (FaceInfo *) file->faces->pdata[i];
1429 if (!g_ascii_strcasecmp (face->family_name, desired.family_name)) {
1430 diff = style_diff (&face->style, &desired);
1431 if (diff < closest) {
1432 closest = diff;
1433 best = face;
1438 file = (FontFile *) file->next;
1441 g_free (desired.family_name);
1443 return best;
1446 FontFace *
1447 FontManager::OpenFontFace (const char *filename, const char *guid, int index)
1449 FT_Open_Args args;
1450 FT_Stream stream;
1451 FontFace *ff;
1452 FT_Face face;
1453 char *key;
1455 key = g_strdup_printf ("%s#%d", filename, index);
1456 if ((ff = (FontFace *) g_hash_table_lookup (faces, key))) {
1457 g_free (key);
1458 ff->ref ();
1459 return ff;
1462 if (!(stream = font_stream_new (filename, guid))) {
1463 g_free (key);
1464 return NULL;
1467 args.flags = FT_OPEN_STREAM;
1468 args.stream = stream;
1470 if (FT_Open_Face (libft2, &args, index, &face) != 0) {
1471 font_stream_destroy (stream);
1472 g_free (key);
1473 return NULL;
1476 if (!FT_IS_SCALABLE (face)) {
1477 FT_Done_Face (face);
1478 font_stream_destroy (stream);
1479 g_free (key);
1480 return NULL;
1483 return new FontFace (this, face, key);
1486 FontFace *
1487 FontManager::OpenFontResource (const char *resource, const char *family, int idx, FontStretches stretch, FontWeights weight, FontStyles style)
1489 FontIndex *index;
1490 FontFile *file;
1491 FontFace *face;
1492 FaceInfo *fi;
1494 LOG_FONT (stderr, "OpenFontResource (\"%s\", \"%s\", %d, %s)\n", resource ? resource : "(null)",
1495 family ? family : "(null)", idx, style_info_to_string (stretch, weight, style));
1497 if (!(index = (FontIndex *) g_hash_table_lookup (resources, resource))) {
1498 LOG_FONT (stderr, " * error: no such resource\n");
1499 return NULL;
1502 if (family != NULL) {
1503 // open by family
1504 if (!(fi = IndexMatchFace (index, family, stretch, weight, style))) {
1505 LOG_FONT (stderr, " * error: resource does not contain requested font\n");
1506 return NULL;
1508 } else if (idx >= 0) {
1509 // open by index
1510 if (!(file = (FontFile *) index->fonts->First ()) || file->next != NULL)
1511 return NULL;
1513 if ((int) file->faces->len <= idx)
1514 return NULL;
1516 fi = (FaceInfo *) file->faces->pdata[idx];
1517 } else {
1518 // no family or index specified... error?
1519 return NULL;
1522 if (!(face = OpenFontFace (fi->file->path, fi->file->guid, fi->index)))
1523 return NULL;
1525 LOG_FONT (stderr, " * opened %s; %s\n", face->GetFamilyName (), face->GetStyleName ());
1527 return face;
1530 FontFace *
1531 FontManager::OpenSystemFont (const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1533 FcPattern *pattern, *matched;
1534 FontStyleInfo desired;
1535 FcChar8 *filename;
1536 FcResult result;
1537 FontFace *face;
1538 int index;
1539 char *key;
1541 key = g_strdup_printf ("%s:%d:%d:%d", family, stretch, weight, style);
1542 LOG_FONT (stderr, "Attempting to open system font: %s %s ... ", family, style_info_to_string (stretch, weight, style));
1543 if (g_hash_table_lookup_extended (system_faces, key, NULL, (gpointer *) &face)) {
1544 LOG_FONT (stderr, "found!\n");
1545 g_free (key);
1546 if (face)
1547 face->ref ();
1548 return face;
1550 LOG_FONT (stderr, "not found in cache.\n");
1552 for (int attempt = 0; attempt < 2; attempt++) {
1553 if (attempt == 0) {
1554 desired.family_name = g_strdup (family);
1555 desired.width = stretch;
1556 desired.weight = weight;
1557 desired.slant = style;
1558 } else {
1559 g_free (desired.family_name);
1560 canon_font_family_and_style (&desired, family, stretch, weight, style);
1563 LOG_FONT (stderr, "Attempting to load installed font: %s %s... ", desired.family_name,
1564 style_info_to_string (desired.width, desired.weight, desired.slant));
1566 pattern = FcPatternCreate ();
1567 FcPatternAddDouble (pattern, FC_DPI, dpi);
1568 FcPatternAddString (pattern, FC_FAMILY, (const FcChar8 *) desired.family_name);
1569 FcPatternAddInteger (pattern, FC_WIDTH, fc_width (desired.width));
1570 FcPatternAddInteger (pattern, FC_WEIGHT, fc_weight (desired.weight));
1571 FcPatternAddInteger (pattern, FC_SLANT, fc_slant (desired.slant));
1572 FcDefaultSubstitute (pattern);
1574 if (!(matched = FcFontMatch (NULL, pattern, &result))) {
1575 LOG_FONT (stderr, "no matches\n");
1576 FcPatternDestroy (pattern);
1577 continue;
1580 FcPatternDestroy (pattern);
1582 if (FcPatternGetString (matched, FC_FILE, 0, &filename) != FcResultMatch) {
1583 LOG_FONT (stderr, "no filename\n");
1584 FcPatternDestroy (matched);
1585 continue;
1588 if (FcPatternGetInteger (matched, FC_INDEX, 0, &index) != FcResultMatch) {
1589 LOG_FONT (stderr, "no index\n");
1590 FcPatternDestroy (matched);
1591 continue;
1594 if ((face = OpenFontFace ((const char *) filename, NULL, index))) {
1595 if (!g_ascii_strcasecmp (face->GetFamilyName (), desired.family_name)) {
1596 LOG_FONT (stderr, "got %s %s\n", face->GetFamilyName (), face->GetStyleName ());
1597 face->ref ();
1598 g_hash_table_insert (system_faces, key, face); // the key is freed when the hash table is destroyed
1599 g_free (desired.family_name);
1600 FcPatternDestroy (matched);
1601 return face;
1604 LOG_FONT (stderr, "family mismatch\n");
1605 face->unref ();
1606 } else {
1607 LOG_FONT (stderr, "family not found\n");
1610 FcPatternDestroy (matched);
1613 g_hash_table_insert (system_faces, key, NULL); // the key is freed when the hash table is destroyed
1614 g_free (desired.family_name);
1616 return NULL;
1619 FontFace *
1620 FontManager::OpenFont (const char *name, FontStretches stretch, FontWeights weight, FontStyles style)
1622 const char *family;
1623 FontFace *face;
1624 //char *end;
1625 //int index;
1627 if ((family = strchr (name, '#'))) {
1628 char *resource = g_strndup (name, family - name);
1630 family++;
1632 //if ((index = strtol (family, &end, 10)) >= 0 && index < G_MAXINT && *end == '\0')
1633 // face = OpenFontResource (resource, NULL, index, stretch, weight, style);
1634 //else
1635 face = OpenFontResource (resource, family, -1, stretch, weight, style);
1637 g_free (resource);
1638 } else {
1639 face = OpenSystemFont (name, stretch, weight, style);
1642 return face;
1645 FontFace *
1646 FontManager::OpenFont (const char *name, int index)
1648 return OpenFontResource (name, NULL, index, FontStretchesNormal, FontWeightsNormal, FontStylesNormal);