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
, 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
;
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
;
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;
860 FontFace::Kerning (double size
, guint32 left
, guint32 right
)
864 if (!FT_HAS_KERNING (face
) || left
== 0 || right
== 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);
877 if (cur_size
!= size
) {
878 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
882 FT_Get_Kerning (face
, left
, right
, FT_KERNING_DEFAULT
, &kerning
);
884 return kerning
.x
/ 64.0;
889 font_move_to (FT_Vector
*to
, void *user_data
)
891 moon_path
*path
= (moon_path
*) user_data
;
894 x
= DOUBLE_FROM_26_6 (to
->x
);
895 y
= DOUBLE_FROM_26_6 (to
->y
);
897 moon_move_to (path
, x
, y
);
903 font_line_to (FT_Vector
*to
, void *user_data
)
905 moon_path
*path
= (moon_path
*) user_data
;
908 x
= DOUBLE_FROM_26_6 (to
->x
);
909 y
= DOUBLE_FROM_26_6 (to
->y
);
911 moon_line_to (path
, x
, y
);
917 font_conic_to (FT_Vector
*control
, FT_Vector
*to
, void *user_data
)
919 moon_path
*path
= (moon_path
*) user_data
;
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
);
935 font_cubic_to (FT_Vector
*control1
, FT_Vector
*control2
, FT_Vector
*to
, void *user_data
)
937 moon_path
*path
= (moon_path
*) user_data
;
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
);
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
,
966 FontFace::LoadGlyph (double size
, GlyphInfo
*glyph
, StyleSimulations simulate
)
968 FT_Glyph_Metrics
*metrics
;
969 FT_Fixed hori_adj
= 0;
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
;
985 if (cur_size
!= size
) {
986 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
993 if (FT_Load_Glyph (face
, glyph
->index
, LOAD_FLAGS
) != 0)
996 if (FT_Render_Glyph (face
->glyph
, FT_RENDER_MODE_NORMAL
) != 0)
999 // invert the glyph over the y-axis and scale
1000 matrix
.xx
= DOUBLE_TO_16_16 (scale
);
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;
1031 font_face_destroy (gpointer data
)
1033 FontFace
*face
= (FontFace
*) data
;
1043 FontManager::FontManager ()
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
)
1058 FcPatternDestroy (pattern
);
1062 FontManager::~FontManager ()
1064 g_hash_table_destroy (resources
);
1065 g_hash_table_destroy (faces
);
1066 FT_Done_FreeType (libft2
);
1075 IndexFontSubdirectory (FT_Library libft2
, const char *name
, GString
*path
, FontIndex
**out
)
1077 FontIndex
*fontdir
= *out
;
1078 const gchar
*dirname
;
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
);
1095 while ((dirname
= g_dir_read_name (dir
))) {
1096 if (!strcmp (dirname
, "..") ||
1097 !strcmp (dirname
, "."))
1100 g_string_append (path
, dirname
);
1102 if (g_stat (path
->str
, &st
) == -1)
1105 if (S_ISDIR (st
.st_mode
)) {
1106 IndexFontSubdirectory (libft2
, name
, path
, &fontdir
);
1110 if (!(stream
= font_stream_new (path
->str
, NULL
)))
1113 args
.flags
= FT_OPEN_STREAM
;
1114 args
.stream
= stream
;
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
);
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
);
1138 if (fontdir
== NULL
)
1139 fontdir
= new FontIndex (name
);
1142 fontdir
->CacheFontInfo (libft2
, path
->str
, stream
, face
, obfuscated
? dirname
: NULL
);
1144 font_stream_destroy (stream
);
1147 g_string_truncate (path
, len
);
1154 return fontdir
!= NULL
;
1158 IndexFontDirectory (FT_Library libft2
, const char *name
, const char *dirname
)
1160 FontIndex
*fontdir
= NULL
;
1164 path
= g_string_new (dirname
);
1167 if (!IndexFontSubdirectory (libft2
, name
, path
, &fontdir
)) {
1168 g_string_free (path
, true);
1172 g_string_truncate (path
, len
);
1173 fontdir
->path
= path
->str
;
1175 g_string_free (path
, false);
1181 IndexFontFile (FT_Library libft2
, const char *name
, const char *path
)
1183 const char *filename
= path_get_basename (name
);
1184 FontIndex
*index
= NULL
;
1190 LOG_FONT (stderr
, " * indexing font file `%s'...\n", path
);
1192 if (!(stream
= font_stream_new (path
, NULL
)))
1195 args
.flags
= FT_OPEN_STREAM
;
1196 args
.stream
= stream
;
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
);
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
);
1220 index
= new FontIndex (name
);
1221 index
->path
= g_strdup (path
);
1224 index
->CacheFontInfo (libft2
, path
, stream
, face
, obfuscated
? filename
: NULL
);
1226 font_stream_destroy (stream
);
1232 FontManager::AddResource (const char *resource
, const char *path
)
1237 LOG_FONT (stderr
, "Adding font resource '%s' at %s\n", resource
, path
);
1239 if ((index
= (FontIndex
*) g_hash_table_lookup (resources
, resource
)))
1242 if (stat (path
, &st
) == -1)
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
);
1253 g_hash_table_insert (resources
, index
->name
, index
);
1257 FontManager::AddResource (ManagedStreamCallbacks
*stream
)
1259 char buf
[4096], *resource
, *dirname
, *path
;
1264 if (!stream
->CanRead (stream
->handle
))
1267 if (!root
&& !(root
= CreateTempDir ("moonlight-fonts")))
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
)
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) {
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) {
1300 // reset the stream to the original state
1301 if (stream
->CanSeek (stream
->handle
) && pos
!= -1)
1302 stream
->Seek (stream
->handle
, pos
, SEEK_SET
);
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) {
1321 // unzip the contents
1322 if (!ExtractAll (zipfile
, dirname
, CanonModeNone
)) {
1323 RemoveDir (dirname
);
1339 AddResource (resource
, path
);
1347 style_diff (FontStyleInfo
*actual
, FontStyleInfo
*desired
)
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
)
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
;
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);
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
;
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
;
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
;
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
) {
1428 file
= (FontFile
*) file
->next
;
1431 g_free (desired
.family_name
);
1437 FontManager::OpenFontFace (const char *filename
, const char *guid
, int index
)
1445 key
= g_strdup_printf ("%s#%d", filename
, index
);
1446 if ((ff
= (FontFace
*) g_hash_table_lookup (faces
, key
))) {
1452 if (!(stream
= font_stream_new (filename
, guid
))) {
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
);
1466 if (!FT_IS_SCALABLE (face
)) {
1467 FT_Done_Face (face
);
1468 font_stream_destroy (stream
);
1473 return new FontFace (this, face
, key
);
1477 FontManager::OpenFontResource (const char *resource
, const char *family
, int idx
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
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");
1492 if (family
!= NULL
) {
1494 if (!(fi
= IndexMatchFace (index
, family
, stretch
, weight
, style
))) {
1495 LOG_FONT (stderr
, " * error: resource does not contain requested font\n");
1498 } else if (idx
>= 0) {
1500 if (!(file
= (FontFile
*) index
->fonts
->First ()) || file
->next
!= NULL
)
1503 if ((int) file
->faces
->len
<= idx
)
1506 fi
= (FaceInfo
*) file
->faces
->pdata
[idx
];
1508 // no family or index specified... error?
1512 if (!(face
= OpenFontFace (fi
->file
->path
, fi
->file
->guid
, fi
->index
)))
1515 LOG_FONT (stderr
, " * opened %s; %s\n", face
->GetFamilyName (), face
->GetStyleName ());
1521 FontManager::OpenSystemFont (const char *family
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1523 FcPattern
*pattern
, *matched
;
1524 FontStyleInfo desired
;
1530 for (int attempt
= 0; attempt
< 2; attempt
++) {
1532 desired
.family_name
= g_strdup (family
);
1533 desired
.width
= stretch
;
1534 desired
.weight
= weight
;
1535 desired
.slant
= style
;
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
);
1558 FcPatternDestroy (pattern
);
1560 if (FcPatternGetString (matched
, FC_FILE
, 0, &filename
) != FcResultMatch
) {
1561 LOG_FONT (stderr
, "no filename\n");
1562 FcPatternDestroy (matched
);
1566 if (FcPatternGetInteger (matched
, FC_INDEX
, 0, &index
) != FcResultMatch
) {
1567 LOG_FONT (stderr
, "no index\n");
1568 FcPatternDestroy (matched
);
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
);
1580 LOG_FONT (stderr
, "family mismatch\n");
1583 LOG_FONT (stderr
, "family not found\n");
1586 FcPatternDestroy (matched
);
1589 g_free (desired
.family_name
);
1595 FontManager::OpenFont (const char *name
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1602 if ((family
= strchr (name
, '#'))) {
1603 char *resource
= g_strndup (name
, family
- name
);
1607 //if ((index = strtol (family, &end, 10)) >= 0 && index < G_MAXINT && *end == '\0')
1608 // face = OpenFontResource (resource, NULL, index, stretch, weight, style);
1610 face
= OpenFontResource (resource
, family
, -1, stretch
, weight
, style
);
1614 face
= OpenSystemFont (name
, stretch
, weight
, style
);
1621 FontManager::OpenFont (const char *name
, int index
)
1623 return OpenFontResource (name
, NULL
, index
, FontStretchesNormal
, FontWeightsNormal
, FontStylesNormal
);