1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
15 #include <glib/gstdio.h>
20 #include "fontmanager.h"
21 #include "zip/unzip.h"
26 #include <fontconfig/fontconfig.h>
27 #include <fontconfig/fcfreetype.h>
28 #include FT_TRUETYPE_TABLES_H
35 // OpenType's OS/2 fsSelection Table:
37 // http://www.microsoft.com/typography/otspec/os2.htm#fss
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
82 #ifndef FC_WEIGHT_ULTRABLACK
83 #define FC_WEIGHT_ULTRABLACK FC_WEIGHT_EXTRABLACK
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
91 FontWeightIsBold (FontWeights weight
)
93 if (weight
> FONT_LOWER_BOLD_LIMIT
)
94 return weight
< 0 || (weight
>= FontWeightsSemiBold
&& weight
< FONT_UPPER_BOLD_LIMIT
);
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
;
121 return FC_WEIGHT_NORMAL
;
125 fc_width (FontStretches 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
;
139 case FontStretchesMedium
:
140 return FC_WIDTH_NORMAL
;
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
;
151 return FC_WIDTH_NORMAL
;
156 fc_slant (FontStyles 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
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
{
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
},
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
},
237 { "Oblique", 7, Slant
, FontStylesOblique
},
238 { "Italic", 6, Slant
, FontStylesItalic
},
239 { "Kursiv", 6, Slant
, FontStylesItalic
},
242 { "Regular", 7, 0, 0 },
243 { "W3", 2, 0, 0 }, // as in Hiragino Mincho Pro W3
247 style_info_parse (const char *style
, FontStyleInfo
*info
, bool family
)
249 register const char *inptr
= style
;
250 const char *first_hint
= NULL
;
260 while (*inptr
&& isspace ((int) ((unsigned char) *inptr
)))
267 while (*inptr
&& !isspace ((int) ((unsigned char) *inptr
)))
272 if (family
&& tokens
== 1) {
273 // if parsing the family_name, first token must not be interpreted as a style hint
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
) {
282 info
->width
= (FontStretches
) style_hints
[i
].value
;
286 info
->weight
= (FontWeights
) style_hints
[i
].value
;
290 info
->slant
= (FontStyles
) style_hints
[i
].value
;
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
;
317 info
->family_name
= g_strndup (style
, first_hint
- style
);
319 info
->family_name
= g_strdup (style
);
321 g_strstrip (info
->family_name
);
327 style_info_to_string (FontStretches stretch
, FontWeights weight
, FontStyles style
)
329 static char namebuf
[256];
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
);
347 if (weight
!= FontWeightsNormal
) {
348 while (style_hints
[i
].type
!= Weight
)
351 while (style_hints
[i
].type
== Weight
) {
352 if (style_hints
[i
].value
== weight
) {
356 p
= g_stpcpy (p
, style_hints
[i
].name
);
364 if (style
!= FontStylesNormal
) {
365 while (style_hints
[i
].type
!= Slant
)
368 while (i
< G_N_ELEMENTS (style_hints
)) {
369 if (style_hints
[i
].value
== style
) {
373 p
= g_stpcpy (p
, style_hints
[i
].name
);
386 is_odttf (const char *name
)
388 size_t len
= strlen (name
);
390 if (len
> 6 && !g_ascii_strcasecmp (name
+ len
- 6, ".odttf"))
408 path_get_basename (const char *path
)
412 if (!(name
= strrchr (path
, '/')))
419 decode_guid (const char *in
, char *guid
)
421 const char *inptr
= in
;
424 while (i
> 0 && *inptr
&& *inptr
!= '.') {
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;
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);
460 font_stream_set_guid (FT_Stream stream
, const char *guid
)
462 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
465 fs
->obfuscated
= decode_guid (guid
, fs
->guid
);
467 fs
->obfuscated
= false;
470 return fs
->obfuscated
;
474 font_stream_read (FT_Stream stream
, unsigned long offset
, unsigned char *buffer
, unsigned long count
)
476 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
479 if (fseek (fs
->fp
, (long) offset
, SEEK_SET
) == -1)
482 if (count
== 0 || buffer
== NULL
)
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
;
492 for ( ; i
< 32 && j
< nread
; i
++, j
++)
493 buffer
[j
] = buffer
[j
] ^ fs
->guid
[i
% 16];
500 font_stream_reset (FT_Stream stream
)
502 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
509 font_stream_close (FT_Stream stream
)
515 font_stream_new (const char *filename
, const char *guid
)
521 if (!(fp
= fopen (filename
, "r")))
524 fs
= (FontStream
*) g_malloc (sizeof (FontStream
));
525 fs
->obfuscated
= false;
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
);
543 font_stream_destroy (FT_Stream stream
)
545 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
557 struct FontFile
: public List::Node
{
561 FontFile (const char *path
, const char *guid
);
571 FaceInfo (FontFile
*file
, FT_Face face
, int index
);
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
;
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
));
601 FaceInfo::~FaceInfo ()
603 g_free (family_name
);
611 FontFile::FontFile (const char *path
, const char *guid
)
613 this->path
= g_strdup (path
);
614 this->guid
= g_strdup (guid
);
618 FontFile::~FontFile ()
623 for (guint i
= 0; i
< faces
->len
; i
++) {
624 face
= (FaceInfo
*) faces
->pdata
[i
];
628 g_ptr_array_free (faces
, true);
645 FontIndex (const char *name
);
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
);
658 FontIndex::~FontIndex ()
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
;
673 LOG_FONT (stderr
, " * caching font info for `%s'...\n", filename
);
675 file
= new FontFile (filename
, guid
);
676 file
->faces
= g_ptr_array_new ();
679 args
.flags
= FT_OPEN_STREAM
;
680 args
.stream
= stream
;
682 if (i
> 0 && FT_Open_Face (libft2
, &args
, i
, &face
) != 0)
685 fi
= new FaceInfo (file
, face
, i
);
686 g_ptr_array_add (file
->faces
, fi
);
690 font_stream_reset (stream
);
693 } while (i
< nfaces
);
695 fonts
->Append (file
);
699 font_index_destroy (gpointer user_data
)
701 delete ((FontIndex
*) user_data
);
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
;
718 g_hash_table_insert (manager
->faces
, key
, this);
721 FontFace::~FontFace ()
725 g_hash_table_steal (manager
->faces
, key
);
727 stream
= face
->stream
;
729 font_stream_destroy (stream
);
749 FontFace::GetFamilyName ()
751 return face
->family_name
;
755 FontFace::GetStyleName ()
757 return face
->style_name
;
761 FontFace::IsScalable ()
763 return FT_IS_SCALABLE (face
);
767 FontFace::IsItalic ()
769 return (face
->style_flags
& FT_STYLE_FLAG_ITALIC
);
775 return (face
->style_flags
& FT_STYLE_FLAG_BOLD
);
779 FontFace::GetCharFromIndex (guint32 index
)
787 unichar
= FT_Get_First_Char (face
, &idx
);
788 while (idx
!= index
&& idx
!= 0)
789 unichar
= FT_Get_Next_Char (face
, unichar
, &idx
);
798 FontFace::GetCharIndex (gunichar unichar
)
800 return FcFreeTypeCharIndex (face
, unichar
);
804 FontFace::HasChar (gunichar unichar
)
806 return FcFreeTypeCharIndex (face
, unichar
) != 0;
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
;
823 height
+= os2
->sTypoLineGap
;
825 descender
= -os2
->sTypoDescender
;
826 ascender
= os2
->sTypoAscender
;
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
;
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;
868 FontFace::Kerning (double size
, guint32 left
, guint32 right
)
872 if (!FT_HAS_KERNING (face
) || left
== 0 || right
== 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);
885 if (cur_size
!= size
) {
886 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
890 FT_Get_Kerning (face
, left
, right
, FT_KERNING_DEFAULT
, &kerning
);
892 return kerning
.x
/ 64.0;
897 font_move_to (FT_Vector
*to
, void *user_data
)
899 moon_path
*path
= (moon_path
*) user_data
;
902 x
= DOUBLE_FROM_26_6 (to
->x
);
903 y
= DOUBLE_FROM_26_6 (to
->y
);
905 moon_move_to (path
, x
, y
);
911 font_line_to (FT_Vector
*to
, void *user_data
)
913 moon_path
*path
= (moon_path
*) user_data
;
916 x
= DOUBLE_FROM_26_6 (to
->x
);
917 y
= DOUBLE_FROM_26_6 (to
->y
);
919 moon_line_to (path
, x
, y
);
925 font_conic_to (FT_Vector
*control
, FT_Vector
*to
, void *user_data
)
927 moon_path
*path
= (moon_path
*) user_data
;
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
);
943 font_cubic_to (FT_Vector
*control1
, FT_Vector
*control2
, FT_Vector
*to
, void *user_data
)
945 moon_path
*path
= (moon_path
*) user_data
;
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
);
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
,
974 FontFace::LoadGlyph (double size
, GlyphInfo
*glyph
, StyleSimulations simulate
)
976 FT_Glyph_Metrics
*metrics
;
977 FT_Fixed hori_adj
= 0;
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
;
993 if (cur_size
!= size
) {
994 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
1001 if (FT_Load_Glyph (face
, glyph
->index
, LOAD_FLAGS
) != 0)
1004 if (FT_Render_Glyph (face
->glyph
, FT_RENDER_MODE_NORMAL
) != 0)
1007 // invert the glyph over the y-axis and scale
1008 matrix
.xx
= DOUBLE_TO_16_16 (scale
);
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;
1039 font_face_destroy (gpointer data
)
1041 FontFace
*face
= (FontFace
*) data
;
1051 FontManager::FontManager ()
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
)
1067 FcPatternDestroy (pattern
);
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
);
1085 IndexFontSubdirectory (FT_Library libft2
, const char *name
, GString
*path
, FontIndex
**out
)
1087 FontIndex
*fontdir
= *out
;
1088 const gchar
*dirname
;
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
);
1105 while ((dirname
= g_dir_read_name (dir
))) {
1106 if (!strcmp (dirname
, "..") ||
1107 !strcmp (dirname
, "."))
1110 g_string_append (path
, dirname
);
1112 if (g_stat (path
->str
, &st
) == -1)
1115 if (S_ISDIR (st
.st_mode
)) {
1116 IndexFontSubdirectory (libft2
, name
, path
, &fontdir
);
1120 if (!(stream
= font_stream_new (path
->str
, NULL
)))
1123 args
.flags
= FT_OPEN_STREAM
;
1124 args
.stream
= stream
;
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
);
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
);
1148 if (fontdir
== NULL
)
1149 fontdir
= new FontIndex (name
);
1152 fontdir
->CacheFontInfo (libft2
, path
->str
, stream
, face
, obfuscated
? dirname
: NULL
);
1154 font_stream_destroy (stream
);
1157 g_string_truncate (path
, len
);
1164 return fontdir
!= NULL
;
1168 IndexFontDirectory (FT_Library libft2
, const char *name
, const char *dirname
)
1170 FontIndex
*fontdir
= NULL
;
1174 path
= g_string_new (dirname
);
1177 if (!IndexFontSubdirectory (libft2
, name
, path
, &fontdir
)) {
1178 g_string_free (path
, true);
1182 g_string_truncate (path
, len
);
1183 fontdir
->path
= path
->str
;
1185 g_string_free (path
, false);
1191 IndexFontFile (FT_Library libft2
, const char *name
, const char *path
)
1193 const char *filename
= path_get_basename (name
);
1194 FontIndex
*index
= NULL
;
1200 LOG_FONT (stderr
, " * indexing font file `%s'...\n", path
);
1202 if (!(stream
= font_stream_new (path
, NULL
)))
1205 args
.flags
= FT_OPEN_STREAM
;
1206 args
.stream
= stream
;
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
);
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
);
1230 index
= new FontIndex (name
);
1231 index
->path
= g_strdup (path
);
1234 index
->CacheFontInfo (libft2
, path
, stream
, face
, obfuscated
? filename
: NULL
);
1236 font_stream_destroy (stream
);
1242 FontManager::AddResource (const char *resource
, const char *path
)
1247 LOG_FONT (stderr
, "Adding font resource '%s' at %s\n", resource
, path
);
1249 if ((index
= (FontIndex
*) g_hash_table_lookup (resources
, resource
)))
1252 if (stat (path
, &st
) == -1)
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
);
1263 g_hash_table_insert (resources
, index
->name
, index
);
1267 FontManager::AddResource (ManagedStreamCallbacks
*stream
)
1269 char buf
[4096], *resource
, *dirname
, *path
;
1274 if (!stream
->CanRead (stream
->handle
))
1277 if (!root
&& !(root
= CreateTempDir ("moonlight-fonts")))
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
)
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) {
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) {
1310 // reset the stream to the original state
1311 if (stream
->CanSeek (stream
->handle
) && pos
!= -1)
1312 stream
->Seek (stream
->handle
, pos
, SEEK_SET
);
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) {
1331 // unzip the contents
1332 if (!ExtractAll (zipfile
, dirname
, CanonModeNone
)) {
1333 RemoveDir (dirname
);
1349 AddResource (resource
, path
);
1357 style_diff (FontStyleInfo
*actual
, FontStyleInfo
*desired
)
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
)
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
;
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);
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
;
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
;
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
;
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
) {
1438 file
= (FontFile
*) file
->next
;
1441 g_free (desired
.family_name
);
1447 FontManager::OpenFontFace (const char *filename
, const char *guid
, int index
)
1455 key
= g_strdup_printf ("%s#%d", filename
, index
);
1456 if ((ff
= (FontFace
*) g_hash_table_lookup (faces
, key
))) {
1462 if (!(stream
= font_stream_new (filename
, guid
))) {
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
);
1476 if (!FT_IS_SCALABLE (face
)) {
1477 FT_Done_Face (face
);
1478 font_stream_destroy (stream
);
1483 return new FontFace (this, face
, key
);
1487 FontManager::OpenFontResource (const char *resource
, const char *family
, int idx
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
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");
1502 if (family
!= NULL
) {
1504 if (!(fi
= IndexMatchFace (index
, family
, stretch
, weight
, style
))) {
1505 LOG_FONT (stderr
, " * error: resource does not contain requested font\n");
1508 } else if (idx
>= 0) {
1510 if (!(file
= (FontFile
*) index
->fonts
->First ()) || file
->next
!= NULL
)
1513 if ((int) file
->faces
->len
<= idx
)
1516 fi
= (FaceInfo
*) file
->faces
->pdata
[idx
];
1518 // no family or index specified... error?
1522 if (!(face
= OpenFontFace (fi
->file
->path
, fi
->file
->guid
, fi
->index
)))
1525 LOG_FONT (stderr
, " * opened %s; %s\n", face
->GetFamilyName (), face
->GetStyleName ());
1531 FontManager::OpenSystemFont (const char *family
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1533 FcPattern
*pattern
, *matched
;
1534 FontStyleInfo desired
;
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");
1550 LOG_FONT (stderr
, "not found in cache.\n");
1552 for (int attempt
= 0; attempt
< 2; attempt
++) {
1554 desired
.family_name
= g_strdup (family
);
1555 desired
.width
= stretch
;
1556 desired
.weight
= weight
;
1557 desired
.slant
= style
;
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
);
1580 FcPatternDestroy (pattern
);
1582 if (FcPatternGetString (matched
, FC_FILE
, 0, &filename
) != FcResultMatch
) {
1583 LOG_FONT (stderr
, "no filename\n");
1584 FcPatternDestroy (matched
);
1588 if (FcPatternGetInteger (matched
, FC_INDEX
, 0, &index
) != FcResultMatch
) {
1589 LOG_FONT (stderr
, "no index\n");
1590 FcPatternDestroy (matched
);
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 ());
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
);
1604 LOG_FONT (stderr
, "family mismatch\n");
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
);
1620 FontManager::OpenFont (const char *name
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1627 if ((family
= strchr (name
, '#'))) {
1628 char *resource
= g_strndup (name
, family
- name
);
1632 //if ((index = strtol (family, &end, 10)) >= 0 && index < G_MAXINT && *end == '\0')
1633 // face = OpenFontResource (resource, NULL, index, stretch, weight, style);
1635 face
= OpenFontResource (resource
, family
, -1, stretch
, weight
, style
);
1639 face
= OpenSystemFont (name
, stretch
, weight
, style
);
1646 FontManager::OpenFont (const char *name
, int index
)
1648 return OpenFontResource (name
, NULL
, index
, FontStretchesNormal
, FontWeightsNormal
, FontStylesNormal
);