in plugin/:
[moon.git] / src / fontmanager.cpp
blobe9ed0b9fd159e9c7c3c35ca6f0f74b45e0e2aa7a
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 DEBUG
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, 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 + os2->sTypoLineGap;
822 descender = -os2->sTypoDescender;
823 ascender = os2->sTypoAscender;
824 } else {
825 // Calculate the LineSpacing for both the hhea table and the OS/2 table.
826 int hhea_height = hhea->Ascender + abs (hhea->Descender) + hhea->Line_Gap;
827 int os2_height = os2 ? (os2->usWinAscent + os2->usWinDescent) : 0;
829 // The LineSpacing is the maximum of the two sumations.
830 height = MAX (hhea_height, os2_height);
832 // If the OS/2 table exists, use usWinAscent as the
833 // ascender. Otherwise use hhea's Ascender value.
834 ascender = os2 ? os2->usWinAscent : hhea->Ascender;
836 // The Descender becomes the difference between the
837 // LineSpacing and the Ascender.
838 descender = height - ascender;
841 extents->descent = -descender * scale;
842 extents->ascent = ascender * scale;
843 extents->height = height * scale;
844 } else {
845 // Fall back to the default FreeType2 values.
846 extents->descent = face->descender * scale;
847 extents->ascent = face->ascender * scale;
848 extents->height = face->height * scale;
851 extents->underline_thickness = face->underline_thickness * scale;
852 extents->underline_position = -face->underline_position * scale;
853 extents->underline_position += ((extents->underline_thickness + 1) / 2.0);
855 if (extents->underline_thickness < 1.0)
856 extents->underline_thickness = 1.0;
859 double
860 FontFace::Kerning (double size, guint32 left, guint32 right)
862 FT_Vector kerning;
864 if (!FT_HAS_KERNING (face) || left == 0 || right == 0)
865 return 0.0;
867 if (size <= FONT_FACE_SIZE) {
868 if (cur_size != FONT_FACE_SIZE) {
869 FT_Set_Pixel_Sizes (face, 0, (int) FONT_FACE_SIZE);
870 cur_size = FONT_FACE_SIZE;
873 FT_Get_Kerning (face, left, right, FT_KERNING_DEFAULT, &kerning);
875 return (kerning.x * size) / (FONT_FACE_SIZE * 64.0);
876 } else {
877 if (cur_size != size) {
878 FT_Set_Pixel_Sizes (face, 0, (int) size);
879 cur_size = size;
882 FT_Get_Kerning (face, left, right, FT_KERNING_DEFAULT, &kerning);
884 return kerning.x / 64.0;
888 static int
889 font_move_to (FT_Vector *to, void *user_data)
891 moon_path *path = (moon_path *) user_data;
892 double x, y;
894 x = DOUBLE_FROM_26_6 (to->x);
895 y = DOUBLE_FROM_26_6 (to->y);
897 moon_move_to (path, x, y);
899 return 0;
902 static int
903 font_line_to (FT_Vector *to, void *user_data)
905 moon_path *path = (moon_path *) user_data;
906 double x, y;
908 x = DOUBLE_FROM_26_6 (to->x);
909 y = DOUBLE_FROM_26_6 (to->y);
911 moon_line_to (path, x, y);
913 return 0;
916 static int
917 font_conic_to (FT_Vector *control, FT_Vector *to, void *user_data)
919 moon_path *path = (moon_path *) user_data;
920 double x3, y3;
921 double x, y;
923 x = DOUBLE_FROM_26_6 (control->x);
924 y = DOUBLE_FROM_26_6 (control->y);
926 x3 = DOUBLE_FROM_26_6 (to->x);
927 y3 = DOUBLE_FROM_26_6 (to->y);
929 moon_quad_curve_to (path, x, y, x3, y3);
931 return 0;
934 static int
935 font_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user_data)
937 moon_path *path = (moon_path *) user_data;
938 double x0, y0;
939 double x1, y1;
940 double x2, y2;
942 x0 = DOUBLE_FROM_26_6 (control1->x);
943 y0 = DOUBLE_FROM_26_6 (control1->y);
945 x1 = DOUBLE_FROM_26_6 (control2->x);
946 y1 = DOUBLE_FROM_26_6 (control2->y);
948 x2 = DOUBLE_FROM_26_6 (to->x);
949 y2 = DOUBLE_FROM_26_6 (to->y);
951 moon_curve_to (path, x0, y0, x1, y1, x2, y2);
953 return 0;
956 static const FT_Outline_Funcs outline_funcs = {
957 (FT_Outline_MoveToFunc) font_move_to,
958 (FT_Outline_LineToFunc) font_line_to,
959 (FT_Outline_ConicToFunc) font_conic_to,
960 (FT_Outline_CubicToFunc) font_cubic_to,
961 0, /* shift */
962 0, /* delta */
965 bool
966 FontFace::LoadGlyph (double size, GlyphInfo *glyph, StyleSimulations simulate)
968 FT_Glyph_Metrics *metrics;
969 FT_Fixed hori_adj = 0;
970 FT_Pos bbox_adj = 0;
971 FT_Matrix matrix;
972 double scale;
974 if (!face)
975 return false;
977 if (size <= FONT_FACE_SIZE) {
978 if (cur_size != FONT_FACE_SIZE) {
979 FT_Set_Pixel_Sizes (face, 0, (int) FONT_FACE_SIZE);
980 cur_size = FONT_FACE_SIZE;
983 scale = size / FONT_FACE_SIZE;
984 } else {
985 if (cur_size != size) {
986 FT_Set_Pixel_Sizes (face, 0, (int) size);
987 cur_size = size;
990 scale = 1.0;
993 if (FT_Load_Glyph (face, glyph->index, LOAD_FLAGS) != 0)
994 return false;
996 if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL) != 0)
997 return false;
999 // invert the glyph over the y-axis and scale
1000 matrix.xx = DOUBLE_TO_16_16 (scale);
1001 matrix.xy = 0;
1002 matrix.yx = 0;
1003 matrix.yy = -DOUBLE_TO_16_16 (scale);
1005 if ((simulate & StyleSimulationsBold) != 0) {
1006 FT_Outline_Embolden (&face->glyph->outline, EMBOLDEN_STRENGTH_26_6);
1007 hori_adj = EMBOLDEN_STRENGTH_16_16;
1008 bbox_adj = EMBOLDEN_STRENGTH_26_6;
1011 if ((simulate & StyleSimulationsItalic) != 0)
1012 FT_Matrix_Multiply (&italicize, &matrix);
1014 glyph->path = moon_path_new (8);
1015 FT_Outline_Transform (&face->glyph->outline, &matrix);
1016 FT_Outline_Decompose (&face->glyph->outline, &outline_funcs, glyph->path);
1018 metrics = &face->glyph->metrics;
1020 glyph->metrics.horiBearingX = DOUBLE_FROM_26_6 (metrics->horiBearingX) * scale;
1021 //glyph->metrics.horiBearingY = DOUBLE_FROM_26_6 (metrics->horiBearingY) * scale;
1022 // always prefer linearHoriAdvance over horiAdvance since the later is rounded to an integer
1023 glyph->metrics.horiAdvance = DOUBLE_FROM_16_16 (face->glyph->linearHoriAdvance + hori_adj) * scale;
1024 //glyph->metrics.height = DOUBLE_FROM_26_6 (metrics->height + bbox_adj) * scale;
1025 //glyph->metrics.width = DOUBLE_FROM_26_6 (metrics->width + bbox_adj) * scale;
1027 return true;
1030 static void
1031 font_face_destroy (gpointer data)
1033 FontFace *face = (FontFace *) data;
1035 delete face;
1040 // FontManager
1043 FontManager::FontManager ()
1045 FcPattern *pattern;
1047 resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, font_index_destroy);
1048 faces = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, font_face_destroy);
1050 FT_Init_FreeType (&libft2);
1052 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "Sans",
1053 FC_SIZE, FcTypeDouble, 10.0, NULL);
1055 if (FcPatternGetDouble (pattern, FC_DPI, 0, &dpi) != FcResultMatch)
1056 dpi = 72.0;
1058 FcPatternDestroy (pattern);
1059 root = NULL;
1062 FontManager::~FontManager ()
1064 g_hash_table_destroy (resources);
1065 g_hash_table_destroy (faces);
1066 FT_Done_FreeType (libft2);
1068 if (root) {
1069 RemoveDir (root);
1070 g_free (root);
1074 static bool
1075 IndexFontSubdirectory (FT_Library libft2, const char *name, GString *path, FontIndex **out)
1077 FontIndex *fontdir = *out;
1078 const gchar *dirname;
1079 FT_Open_Args args;
1080 FT_Stream stream;
1081 bool obfuscated;
1082 struct stat st;
1083 FT_Face face;
1084 size_t len;
1085 GDir *dir;
1087 if (!(dir = g_dir_open (path->str, 0, NULL)))
1088 return fontdir != NULL;
1090 LOG_FONT (stderr, " * indexing font directory `%s'...\n", path->str);
1092 g_string_append_c (path, G_DIR_SEPARATOR);
1093 len = path->len;
1095 while ((dirname = g_dir_read_name (dir))) {
1096 if (!strcmp (dirname, "..") ||
1097 !strcmp (dirname, "."))
1098 continue;
1100 g_string_append (path, dirname);
1102 if (g_stat (path->str, &st) == -1)
1103 goto next;
1105 if (S_ISDIR (st.st_mode)) {
1106 IndexFontSubdirectory (libft2, name, path, &fontdir);
1107 goto next;
1110 if (!(stream = font_stream_new (path->str, NULL)))
1111 goto next;
1113 args.flags = FT_OPEN_STREAM;
1114 args.stream = stream;
1116 obfuscated = false;
1118 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1119 // not a valid font file... is it maybe an obfuscated font?
1120 if (!is_odttf (dirname) || !font_stream_set_guid (stream, dirname)) {
1121 font_stream_destroy (stream);
1122 goto next;
1125 font_stream_reset (stream);
1127 args.flags = FT_OPEN_STREAM;
1128 args.stream = stream;
1130 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1131 font_stream_destroy (stream);
1132 goto next;
1135 obfuscated = true;
1138 if (fontdir == NULL)
1139 fontdir = new FontIndex (name);
1141 // cache font info
1142 fontdir->CacheFontInfo (libft2, path->str, stream, face, obfuscated ? dirname : NULL);
1144 font_stream_destroy (stream);
1146 next:
1147 g_string_truncate (path, len);
1150 g_dir_close (dir);
1152 *out = fontdir;
1154 return fontdir != NULL;
1157 static FontIndex *
1158 IndexFontDirectory (FT_Library libft2, const char *name, const char *dirname)
1160 FontIndex *fontdir = NULL;
1161 GString *path;
1162 size_t len;
1164 path = g_string_new (dirname);
1165 len = path->len;
1167 if (!IndexFontSubdirectory (libft2, name, path, &fontdir)) {
1168 g_string_free (path, true);
1169 return NULL;
1172 g_string_truncate (path, len);
1173 fontdir->path = path->str;
1175 g_string_free (path, false);
1177 return fontdir;
1180 static FontIndex *
1181 IndexFontFile (FT_Library libft2, const char *name, const char *path)
1183 const char *filename = path_get_basename (name);
1184 FontIndex *index = NULL;
1185 FT_Open_Args args;
1186 FT_Stream stream;
1187 bool obfuscated;
1188 FT_Face face;
1190 LOG_FONT (stderr, " * indexing font file `%s'...\n", path);
1192 if (!(stream = font_stream_new (path, NULL)))
1193 return NULL;
1195 args.flags = FT_OPEN_STREAM;
1196 args.stream = stream;
1198 obfuscated = false;
1200 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1201 // not a valid font file... is it maybe an obfuscated font?
1202 if (!is_odttf (filename) || !font_stream_set_guid (stream, filename)) {
1203 font_stream_destroy (stream);
1204 return NULL;
1207 font_stream_reset (stream);
1209 args.flags = FT_OPEN_STREAM;
1210 args.stream = stream;
1212 if (FT_Open_Face (libft2, &args, 0, &face) != 0) {
1213 font_stream_destroy (stream);
1214 return NULL;
1217 obfuscated = true;
1220 index = new FontIndex (name);
1221 index->path = g_strdup (path);
1223 // cache font info
1224 index->CacheFontInfo (libft2, path, stream, face, obfuscated ? filename : NULL);
1226 font_stream_destroy (stream);
1228 return index;
1231 void
1232 FontManager::AddResource (const char *resource, const char *path)
1234 FontIndex *index;
1235 struct stat st;
1237 LOG_FONT (stderr, "Adding font resource '%s' at %s\n", resource, path);
1239 if ((index = (FontIndex *) g_hash_table_lookup (resources, resource)))
1240 return;
1242 if (stat (path, &st) == -1)
1243 return;
1245 if (S_ISDIR (st.st_mode))
1246 index = IndexFontDirectory (libft2, resource, path);
1247 else if (S_ISREG (st.st_mode))
1248 index = IndexFontFile (libft2, resource, path);
1249 else
1250 return;
1252 if (index)
1253 g_hash_table_insert (resources, index->name, index);
1256 char *
1257 FontManager::AddResource (ManagedStreamCallbacks *stream)
1259 char buf[4096], *resource, *dirname, *path;
1260 unzFile zipfile;
1261 int nread, fd;
1262 gint64 pos;
1264 if (!stream->CanRead (stream->handle))
1265 return NULL;
1267 if (!root && !(root = CreateTempDir ("moonlight-fonts")))
1268 return NULL;
1270 // check if we've already added this resource
1271 resource = g_strdup_printf ("font-source://%p", stream->handle);
1272 if (g_hash_table_lookup (resources, resource) != NULL)
1273 return resource;
1275 snprintf (buf, sizeof (buf), "%p", stream->handle);
1276 path = g_build_filename (root, buf, NULL);
1278 if ((fd = g_open (path, O_CREAT | O_EXCL | O_WRONLY, 0600)) == -1) {
1279 g_free (resource);
1280 g_free (path);
1281 return NULL;
1284 // write the managed stream to disk
1285 pos = stream->Position (stream->handle);
1287 if (stream->CanSeek (stream->handle))
1288 stream->Seek (stream->handle, 0, SEEK_SET);
1290 while ((nread = stream->Read (stream->handle, buf, 0, sizeof (buf))) > 0) {
1291 if (write_all (fd, buf, (size_t) nread) == -1) {
1292 g_free (resource);
1293 close (fd);
1294 g_unlink (path);
1295 g_free (path);
1296 return NULL;
1300 // reset the stream to the original state
1301 if (stream->CanSeek (stream->handle) && pos != -1)
1302 stream->Seek (stream->handle, pos, SEEK_SET);
1304 close (fd);
1306 // check to see if the resource is zipped
1307 if ((zipfile = unzOpen (path))) {
1308 snprintf (buf, sizeof (buf), "%p.zip", stream->handle);
1309 dirname = g_build_filename (root, buf, NULL);
1311 // create a directory to contain our unzipped content
1312 if (g_mkdir (dirname, 0700) == -1) {
1313 unzClose (zipfile);
1314 g_free (resource);
1315 g_free (dirname);
1316 g_unlink (path);
1317 g_free (path);
1318 return NULL;
1321 // unzip the contents
1322 if (!ExtractAll (zipfile, dirname, CanonModeNone)) {
1323 RemoveDir (dirname);
1324 unzClose (zipfile);
1325 g_free (resource);
1326 g_free (dirname);
1327 g_unlink (path);
1328 g_free (path);
1329 return NULL;
1332 unzClose (zipfile);
1333 g_unlink (path);
1334 g_free (path);
1336 path = dirname;
1339 AddResource (resource, path);
1341 g_free (path);
1343 return resource;
1346 static int
1347 style_diff (FontStyleInfo *actual, FontStyleInfo *desired)
1349 #if 0
1350 // we convert to FontConfig for 2 reasons:
1351 // 1. negative values and values > 1023
1352 // 2. smaller ranges
1353 int weight = abs (fc_weight (actual->weight) - fc_weight (desired->weight));
1355 if (actual->slant == desired->slant)
1356 return weight;
1358 if (actual->slant == FontStylesNormal) {
1359 // we can emulate italic/oblique, but we would still prefer the real
1360 // italic font if we can find it so apply a slight penalty
1361 return 1000 + weight;
1364 // ouch, apply a huge penalty
1365 return 1000000 + weight;
1366 #else
1367 // convert to FontConfig values so that each style property fits within 8 bits
1368 int weight = abs (fc_weight (actual->weight) - fc_weight (desired->weight));
1369 int width = abs (fc_width (actual->width) - fc_width (desired->width));
1370 int slant = abs (fc_slant (actual->slant) - fc_slant (desired->slant));
1372 // weight has the highest priority, followed by weight and then slant
1373 return ((width & 0xff) << 16) | ((weight & 0xff) << 8) | (slant & 0xff);
1374 #endif
1377 static void
1378 canon_font_family_and_style (FontStyleInfo *desired, const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1380 desired->width = FontStretchesNormal;
1381 desired->weight = FontWeightsNormal;
1382 desired->slant = FontStylesNormal;
1383 desired->family_name = NULL;
1384 desired->set = 0;
1386 // extract whatever little style info we can from the family name
1387 style_info_parse (family, desired, true);
1389 // override style with user-specified attributes
1390 if (!(desired->set & Width))
1391 desired->width = stretch;
1392 if (!(desired->set & Weight))
1393 desired->weight = weight;
1394 if (!(desired->set & Slant))
1395 desired->slant = style;
1398 static FaceInfo *
1399 IndexMatchFace (FontIndex *index, const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1401 FontFile *file = (FontFile *) index->fonts->First ();
1402 FaceInfo *face, *best = NULL;
1403 FontStyleInfo desired;
1404 int closest = G_MAXINT;
1405 int diff;
1406 guint i;
1408 LOG_FONT (stderr, " * searching index for %s; %s\n", family, style_info_to_string (stretch, weight, style));
1410 canon_font_family_and_style (&desired, family, stretch, weight, style);
1412 LOG_FONT (stderr, " * canonicalized family/style: %s; %s\n", desired.family_name,
1413 style_info_to_string (desired.width, desired.weight, desired.slant));
1415 while (file != NULL) {
1416 for (i = 0; i < file->faces->len; i++) {
1417 face = (FaceInfo *) file->faces->pdata[i];
1419 if (!g_ascii_strcasecmp (face->family_name, desired.family_name)) {
1420 diff = style_diff (&face->style, &desired);
1421 if (diff < closest) {
1422 closest = diff;
1423 best = face;
1428 file = (FontFile *) file->next;
1431 g_free (desired.family_name);
1433 return best;
1436 FontFace *
1437 FontManager::OpenFontFace (const char *filename, const char *guid, int index)
1439 FT_Open_Args args;
1440 FT_Stream stream;
1441 FontFace *ff;
1442 FT_Face face;
1443 char *key;
1445 key = g_strdup_printf ("%s#%d", filename, index);
1446 if ((ff = (FontFace *) g_hash_table_lookup (faces, key))) {
1447 g_free (key);
1448 ff->ref ();
1449 return ff;
1452 if (!(stream = font_stream_new (filename, guid))) {
1453 g_free (key);
1454 return NULL;
1457 args.flags = FT_OPEN_STREAM;
1458 args.stream = stream;
1460 if (FT_Open_Face (libft2, &args, index, &face) != 0) {
1461 font_stream_destroy (stream);
1462 g_free (key);
1463 return NULL;
1466 if (!FT_IS_SCALABLE (face)) {
1467 FT_Done_Face (face);
1468 font_stream_destroy (stream);
1469 g_free (key);
1470 return NULL;
1473 return new FontFace (this, face, key);
1476 FontFace *
1477 FontManager::OpenFontResource (const char *resource, const char *family, int idx, FontStretches stretch, FontWeights weight, FontStyles style)
1479 FontIndex *index;
1480 FontFile *file;
1481 FontFace *face;
1482 FaceInfo *fi;
1484 LOG_FONT (stderr, "OpenFontResource (\"%s\", \"%s\", %d, %s)\n", resource ? resource : "(null)",
1485 family ? family : "(null)", idx, style_info_to_string (stretch, weight, style));
1487 if (!(index = (FontIndex *) g_hash_table_lookup (resources, resource))) {
1488 LOG_FONT (stderr, " * error: no such resource\n");
1489 return NULL;
1492 if (family != NULL) {
1493 // open by family
1494 if (!(fi = IndexMatchFace (index, family, stretch, weight, style))) {
1495 LOG_FONT (stderr, " * error: resource does not contain requested font\n");
1496 return NULL;
1498 } else if (idx >= 0) {
1499 // open by index
1500 if (!(file = (FontFile *) index->fonts->First ()) || file->next != NULL)
1501 return NULL;
1503 if ((int) file->faces->len <= idx)
1504 return NULL;
1506 fi = (FaceInfo *) file->faces->pdata[idx];
1507 } else {
1508 // no family or index specified... error?
1509 return NULL;
1512 if (!(face = OpenFontFace (fi->file->path, fi->file->guid, fi->index)))
1513 return NULL;
1515 LOG_FONT (stderr, " * opened %s; %s\n", face->GetFamilyName (), face->GetStyleName ());
1517 return face;
1520 FontFace *
1521 FontManager::OpenSystemFont (const char *family, FontStretches stretch, FontWeights weight, FontStyles style)
1523 FcPattern *pattern, *matched;
1524 FontStyleInfo desired;
1525 FcChar8 *filename;
1526 FcResult result;
1527 FontFace *face;
1528 int index;
1530 for (int attempt = 0; attempt < 2; attempt++) {
1531 if (attempt == 0) {
1532 desired.family_name = g_strdup (family);
1533 desired.width = stretch;
1534 desired.weight = weight;
1535 desired.slant = style;
1536 } else {
1537 g_free (desired.family_name);
1538 canon_font_family_and_style (&desired, family, stretch, weight, style);
1541 LOG_FONT (stderr, "Attempting to load installed font: %s %s... ", desired.family_name,
1542 style_info_to_string (desired.width, desired.weight, desired.slant));
1544 pattern = FcPatternCreate ();
1545 FcPatternAddDouble (pattern, FC_DPI, dpi);
1546 FcPatternAddString (pattern, FC_FAMILY, (const FcChar8 *) desired.family_name);
1547 FcPatternAddInteger (pattern, FC_WIDTH, fc_width (desired.width));
1548 FcPatternAddInteger (pattern, FC_WEIGHT, fc_weight (desired.weight));
1549 FcPatternAddInteger (pattern, FC_SLANT, fc_slant (desired.slant));
1550 FcDefaultSubstitute (pattern);
1552 if (!(matched = FcFontMatch (NULL, pattern, &result))) {
1553 LOG_FONT (stderr, "no matches\n");
1554 FcPatternDestroy (pattern);
1555 continue;
1558 FcPatternDestroy (pattern);
1560 if (FcPatternGetString (matched, FC_FILE, 0, &filename) != FcResultMatch) {
1561 LOG_FONT (stderr, "no filename\n");
1562 FcPatternDestroy (matched);
1563 continue;
1566 if (FcPatternGetInteger (matched, FC_INDEX, 0, &index) != FcResultMatch) {
1567 LOG_FONT (stderr, "no index\n");
1568 FcPatternDestroy (matched);
1569 continue;
1572 if ((face = OpenFontFace ((const char *) filename, NULL, index))) {
1573 if (!g_ascii_strcasecmp (face->GetFamilyName (), desired.family_name)) {
1574 LOG_FONT (stderr, "got %s %s\n", face->GetFamilyName (), face->GetStyleName ());
1575 g_free (desired.family_name);
1576 FcPatternDestroy (matched);
1577 return face;
1580 LOG_FONT (stderr, "family mismatch\n");
1581 face->unref ();
1582 } else {
1583 LOG_FONT (stderr, "family not found\n");
1586 FcPatternDestroy (matched);
1589 g_free (desired.family_name);
1591 return NULL;
1594 FontFace *
1595 FontManager::OpenFont (const char *name, FontStretches stretch, FontWeights weight, FontStyles style)
1597 const char *family;
1598 FontFace *face;
1599 //char *end;
1600 //int index;
1602 if ((family = strchr (name, '#'))) {
1603 char *resource = g_strndup (name, family - name);
1605 family++;
1607 //if ((index = strtol (family, &end, 10)) >= 0 && index < G_MAXINT && *end == '\0')
1608 // face = OpenFontResource (resource, NULL, index, stretch, weight, style);
1609 //else
1610 face = OpenFontResource (resource, family, -1, stretch, weight, style);
1612 g_free (resource);
1613 } else {
1614 face = OpenSystemFont (name, stretch, weight, style);
1617 return face;
1620 FontFace *
1621 FontManager::OpenFont (const char *name, int index)
1623 return OpenFontResource (name, NULL, index, FontStretchesNormal, FontWeightsNormal, FontStylesNormal);