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.
16 #include <glib/gstdio.h>
21 #include <sys/types.h>
29 #include "fontmanager.h"
30 #include "zip/unzip.h"
35 #include <fontconfig/fontconfig.h>
36 #include <fontconfig/fcfreetype.h>
37 #include FT_TRUETYPE_TABLES_H
44 // OpenType's OS/2 fsSelection Table:
46 // http://www.microsoft.com/typography/otspec/os2.htm#fss
49 fsSelectionItalic
= (1 << 0),
50 fsSelectionUnderscore
= (1 << 1),
51 fsSelectionNegative
= (1 << 2),
52 fsSelectionOutlined
= (1 << 3),
53 fsSelectionStrikeout
= (1 << 4),
54 fsSelectionBold
= (1 << 5),
55 fsSelectionRegular
= (1 << 6),
56 fsSelectionUseTypoMetrics
= (1 << 7),
57 fsSelectionWWS
= (1 << 8),
58 fsSelectionOblique
= (1 << 9),
61 #define FONT_FACE_SIZE 41.0
63 #define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
64 #define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
65 #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
66 #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)
68 #define EMBOLDEN_STRENGTH 0.75
69 #define EMBOLDEN_STRENGTH_26_6 DOUBLE_TO_26_6 (EMBOLDEN_STRENGTH)
70 #define EMBOLDEN_STRENGTH_16_16 DOUBLE_TO_16_16 (EMBOLDEN_STRENGTH)
72 #define ITALIC_SLANT -17.5
73 #define ITALIC_SLANT_RADIANS (ITALIC_SLANT * M_PI / 180.0)
75 static const FT_Matrix italicize
= {
76 DOUBLE_TO_16_16 (1.0), DOUBLE_TO_16_16 (tan (ITALIC_SLANT_RADIANS
)),
77 DOUBLE_TO_16_16 (0.0), DOUBLE_TO_16_16 (1.0)
80 #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)
85 // Silverlight -> FontConfig enumeration conversion utilities
88 #ifndef FC_WEIGHT_EXTRABLACK
89 #define FC_WEIGHT_EXTRABLACK 215
91 #ifndef FC_WEIGHT_ULTRABLACK
92 #define FC_WEIGHT_ULTRABLACK FC_WEIGHT_EXTRABLACK
95 // Silverlight accepts negative values ]0,-475[ as bold and everything over 1023 as normal
96 #define FONT_LOWER_BOLD_LIMIT -475
97 #define FONT_UPPER_BOLD_LIMIT 1024
100 FontWeightIsBold (FontWeights weight
)
102 if (weight
> FONT_LOWER_BOLD_LIMIT
)
103 return weight
< 0 || (weight
>= FontWeightsSemiBold
&& weight
< FONT_UPPER_BOLD_LIMIT
);
109 fc_weight (FontWeights weight
)
111 if ((weight
< 0) && (weight
> FONT_LOWER_BOLD_LIMIT
))
112 return FC_WEIGHT_BLACK
;
113 else if (weight
< (FontWeightsThin
+ FontWeightsLight
) / 2)
114 return FC_WEIGHT_ULTRALIGHT
;
115 else if (weight
< (FontWeightsLight
+ FontWeightsNormal
) / 2)
116 return FC_WEIGHT_LIGHT
;
117 else if (weight
< (FontWeightsNormal
+ FontWeightsMedium
) / 2)
118 return FC_WEIGHT_NORMAL
;
119 else if (weight
< (FontWeightsMedium
+ FontWeightsSemiBold
) / 2)
120 return FC_WEIGHT_MEDIUM
;
121 else if (weight
< (FontWeightsSemiBold
+ FontWeightsBold
) / 2)
122 return FC_WEIGHT_SEMIBOLD
;
123 else if (weight
< (FontWeightsBold
+ FontWeightsExtraBold
) / 2)
124 return FC_WEIGHT_BOLD
;
125 else if (weight
< (FontWeightsExtraBold
+ FontWeightsBlack
) / 2)
126 return FC_WEIGHT_ULTRABOLD
;
127 else if (weight
< FONT_UPPER_BOLD_LIMIT
)
128 return FC_WEIGHT_BLACK
;
130 return FC_WEIGHT_NORMAL
;
134 fc_width (FontStretches stretch
)
137 case FontStretchesUltraCondensed
:
138 return FC_WIDTH_ULTRACONDENSED
;
139 case FontStretchesExtraCondensed
:
140 return FC_WIDTH_EXTRACONDENSED
;
141 case FontStretchesCondensed
:
142 return FC_WIDTH_CONDENSED
;
143 case FontStretchesSemiCondensed
:
144 return FC_WIDTH_SEMICONDENSED
;
145 case FontStretchesNormal
:
146 return FC_WIDTH_NORMAL
;
148 case FontStretchesMedium
:
149 return FC_WIDTH_NORMAL
;
151 case FontStretchesSemiExpanded
:
152 return FC_WIDTH_SEMIEXPANDED
;
153 case FontStretchesExpanded
:
154 return FC_WIDTH_EXPANDED
;
155 case FontStretchesExtraExpanded
:
156 return FC_WIDTH_EXTRAEXPANDED
;
157 case FontStretchesUltraExpanded
:
158 return FC_WIDTH_ULTRAEXPANDED
;
160 return FC_WIDTH_NORMAL
;
165 fc_slant (FontStyles style
)
168 case FontStylesNormal
:
169 return FC_SLANT_ROMAN
;
170 // technically Olbique does not exists in SL 1.0 or 2.0 (it's in WPF) but the parser allows it
171 case FontStylesOblique
:
172 return FC_SLANT_OBLIQUE
;
173 case FontStylesItalic
:
174 // Silverlight defaults bad values to Italic
176 return FC_SLANT_ITALIC
;
182 // Font-style parser utils
185 #define Width (1 << 0)
186 #define Weight (1 << 1)
187 #define Slant (1 << 2)
189 struct FontStyleInfo
{
204 { "Ultra-Condensed", 15, Width
, FontStretchesUltraCondensed
},
205 { "Extra-Condensed", 15, Width
, FontStretchesExtraCondensed
},
206 { "Semi-Condensed", 14, Width
, FontStretchesSemiCondensed
},
207 { "UltraCondensed", 14, Width
, FontStretchesUltraCondensed
},
208 { "ExtraCondensed", 14, Width
, FontStretchesExtraCondensed
},
209 { "SemiCondensed", 13, Width
, FontStretchesSemiCondensed
},
210 { "Condensed", 9, Width
, FontStretchesCondensed
},
211 { "Cond", 4, Width
, FontStretchesCondensed
},
212 { "Ultra-Expanded", 14, Width
, FontStretchesUltraExpanded
},
213 { "Extra-Expanded", 14, Width
, FontStretchesExtraExpanded
},
214 { "Semi-Expanded", 13, Width
, FontStretchesSemiExpanded
},
215 { "UltraExpanded", 13, Width
, FontStretchesUltraExpanded
},
216 { "ExtraExpanded", 13, Width
, FontStretchesExtraExpanded
},
217 { "SemiExpanded", 12, Width
, FontStretchesSemiExpanded
},
218 { "Expanded", 8, Width
, FontStretchesExpanded
},
221 { "Thin", 4, Weight
, FontWeightsThin
},
222 { "Ultra-Light", 11, Weight
, FontWeightsExtraLight
},
223 { "Extra-Light", 11, Weight
, FontWeightsExtraLight
},
224 { "UltraLight", 10, Weight
, FontWeightsExtraLight
},
225 { "ExtraLight", 10, Weight
, FontWeightsExtraLight
},
226 { "Light", 5, Weight
, FontWeightsLight
},
227 { "Book", 4, Weight
, FontWeightsNormal
},
228 { "Medium", 6, Weight
, FontWeightsMedium
},
229 { "Demi-Bold", 9, Weight
, FontWeightsSemiBold
},
230 { "Semi-Bold", 9, Weight
, FontWeightsSemiBold
},
231 { "DemiBold", 8, Weight
, FontWeightsSemiBold
},
232 { "SemiBold", 8, Weight
, FontWeightsSemiBold
},
233 { "Bold", 4, Weight
, FontWeightsBold
},
234 { "Extra-Bold", 10, Weight
, FontWeightsExtraBold
},
235 { "Ultra-Bold", 10, Weight
, FontWeightsExtraBold
},
236 { "ExtraBold", 9, Weight
, FontWeightsExtraBold
},
237 { "UltraBold", 9, Weight
, FontWeightsExtraBold
},
238 { "Black", 5, Weight
, FontWeightsBlack
},
239 { "Heavy", 5, Weight
, FontWeightsBlack
},
240 { "Extra-Black", 11, Weight
, FontWeightsExtraBlack
},
241 { "Ultra-Black", 11, Weight
, FontWeightsExtraBlack
},
242 { "ExtraBlack", 10, Weight
, FontWeightsExtraBlack
},
243 { "UltraBlack", 10, Weight
, FontWeightsExtraBlack
},
246 { "Oblique", 7, Slant
, FontStylesOblique
},
247 { "Italic", 6, Slant
, FontStylesItalic
},
248 { "Kursiv", 6, Slant
, FontStylesItalic
},
251 { "Regular", 7, 0, 0 },
252 { "W3", 2, 0, 0 }, // as in Hiragino Mincho Pro W3
256 style_info_parse (const char *style
, FontStyleInfo
*info
, bool family
)
258 register const char *inptr
= style
;
259 const char *first_hint
= NULL
;
269 while (*inptr
&& isspace ((int) ((unsigned char) *inptr
)))
276 while (*inptr
&& !isspace ((int) ((unsigned char) *inptr
)))
281 if (family
&& tokens
== 1) {
282 // if parsing the family_name, first token must not be interpreted as a style hint
286 len
= (size_t) (inptr
- token
);
287 for (i
= 0; i
< G_N_ELEMENTS (style_hints
); i
++) {
288 if (style_hints
[i
].len
== len
&& !strncmp (style_hints
[i
].name
, token
, len
)) {
289 switch (style_hints
[i
].type
) {
291 info
->width
= (FontStretches
) style_hints
[i
].value
;
295 info
->weight
= (FontWeights
) style_hints
[i
].value
;
299 info
->slant
= (FontStyles
) style_hints
[i
].value
;
310 if (family
&& i
== G_N_ELEMENTS (style_hints
)) {
311 // if we come across an unknown style hint when
312 // parsing the family_name, assume that any previously
313 // found style hints were not actually style hints,
314 // but instead just part of the family name.
315 info
->width
= FontStretchesNormal
;
316 info
->weight
= FontWeightsNormal
;
317 info
->slant
= FontStylesNormal
;
326 info
->family_name
= g_strndup (style
, first_hint
- style
);
328 info
->family_name
= g_strdup (style
);
330 g_strstrip (info
->family_name
);
336 style_info_to_string (FontStretches stretch
, FontWeights weight
, FontStyles style
)
338 static char namebuf
[256];
345 if (stretch
!= FontStretchesNormal
) {
346 while (style_hints
[i
].type
== Width
) {
347 if (style_hints
[i
].value
== stretch
) {
348 p
= g_stpcpy (p
, style_hints
[i
].name
);
356 if (weight
!= FontWeightsNormal
) {
357 while (style_hints
[i
].type
!= Weight
)
360 while (style_hints
[i
].type
== Weight
) {
361 if (style_hints
[i
].value
== weight
) {
365 p
= g_stpcpy (p
, style_hints
[i
].name
);
373 if (style
!= FontStylesNormal
) {
374 while (style_hints
[i
].type
!= Slant
)
377 while (i
< G_N_ELEMENTS (style_hints
)) {
378 if (style_hints
[i
].value
== style
) {
382 p
= g_stpcpy (p
, style_hints
[i
].name
);
395 is_odttf (const char *name
)
397 size_t len
= strlen (name
);
399 if (len
> 6 && !g_ascii_strcasecmp (name
+ len
- 6, ".odttf"))
417 path_get_basename (const char *path
)
421 if (!(name
= strrchr (path
, '/')))
428 decode_guid (const char *in
, char *guid
)
430 const char *inptr
= in
;
433 while (i
> 0 && *inptr
&& *inptr
!= '.') {
439 if (*inptr
>= '0' && *inptr
<= '9')
440 guid
[i
] = (*inptr
- '0') * 16;
441 else if (*inptr
>= 'a' && *inptr
<= 'f')
442 guid
[i
] = ((*inptr
- 'a') + 10) * 16;
443 else if (*inptr
>= 'A' && *inptr
<= 'F')
444 guid
[i
] = ((*inptr
- 'A') + 10) * 16;
450 if (*inptr
>= '0' && *inptr
<= '9')
451 guid
[i
] += (*inptr
- '0');
452 else if (*inptr
>= 'a' && *inptr
<= 'f')
453 guid
[i
] += ((*inptr
- 'a') + 10);
454 else if (*inptr
>= 'A' && *inptr
<= 'F')
455 guid
[i
] += ((*inptr
- 'A') + 10);
469 font_stream_set_guid (FT_Stream stream
, const char *guid
)
471 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
474 fs
->obfuscated
= decode_guid (guid
, fs
->guid
);
476 fs
->obfuscated
= false;
479 return fs
->obfuscated
;
483 font_stream_read (FT_Stream stream
, unsigned long offset
, unsigned char *buffer
, unsigned long count
)
485 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
488 if (fseek (fs
->fp
, (long) offset
, SEEK_SET
) == -1)
491 if (count
== 0 || buffer
== NULL
)
494 nread
= fread (buffer
, 1, count
, fs
->fp
);
496 if (fs
->obfuscated
&& offset
< 32 && nread
> 0) {
497 /* obfuscated font... need to deobfuscate */
498 unsigned long i
= offset
;
501 for ( ; i
< 32 && j
< nread
; i
++, j
++)
502 buffer
[j
] = buffer
[j
] ^ fs
->guid
[i
% 16];
509 font_stream_reset (FT_Stream stream
)
511 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
518 font_stream_close (FT_Stream stream
)
524 font_stream_new (const char *filename
, const char *guid
)
530 if (!(fp
= fopen (filename
, "r")))
533 fs
= (FontStream
*) g_malloc (sizeof (FontStream
));
534 fs
->obfuscated
= false;
537 stream
= (FT_Stream
) g_malloc0 (sizeof (FT_StreamRec
));
538 stream
->close
= font_stream_close
;
539 stream
->read
= font_stream_read
;
540 stream
->descriptor
.pointer
= fs
;
542 fseek (fp
, 0, SEEK_END
);
543 stream
->size
= ftell (fp
);
544 fseek (fp
, 0, SEEK_SET
);
546 font_stream_set_guid (stream
, guid
);
552 font_stream_destroy (FT_Stream stream
)
554 FontStream
*fs
= (FontStream
*) stream
->descriptor
.pointer
;
566 struct FontFile
: public List::Node
{
570 FontFile (const char *path
, const char *guid
);
580 FaceInfo (FontFile
*file
, FT_Face face
, int index
);
584 FaceInfo::FaceInfo (FontFile
*file
, FT_Face face
, int index
)
586 LOG_FONT (stderr
, " * indexing %s[%d]: family=\"%s\"; style=\"%s\"\n",
587 path_get_basename (file
->path
), index
, face
->family_name
, face
->style_name
);
589 style
.width
= FontStretchesNormal
;
590 style
.weight
= FontWeightsNormal
;
591 style
.slant
= FontStylesNormal
;
592 style
.family_name
= NULL
;
595 // extract whatever little style info we can from the family name
596 style_info_parse (face
->family_name
, &style
, true);
598 // style info parsed from style_name overrides anything we got from family_name
599 style_info_parse (face
->style_name
, &style
, false);
601 family_name
= style
.family_name
;
603 LOG_FONT (stderr
, " * indexed as %s; %s\n", family_name
,
604 style_info_to_string (style
.width
, style
.weight
, style
.slant
));
610 FaceInfo::~FaceInfo ()
612 g_free (family_name
);
620 FontFile::FontFile (const char *path
, const char *guid
)
622 this->path
= g_strdup (path
);
623 this->guid
= g_strdup (guid
);
627 FontFile::~FontFile ()
632 for (guint i
= 0; i
< faces
->len
; i
++) {
633 face
= (FaceInfo
*) faces
->pdata
[i
];
637 g_ptr_array_free (faces
, true);
654 FontIndex (const char *name
);
657 void CacheFontInfo (FT_Library libft2
, const char *filename
, FT_Stream stream
, FT_Face face
, const char *guid
);
660 FontIndex::FontIndex (const char *name
)
662 this->name
= g_strdup (name
);
667 FontIndex::~FontIndex ()
675 FontIndex::CacheFontInfo (FT_Library libft2
, const char *filename
, FT_Stream stream
, FT_Face face
, const char *guid
)
677 int i
= 0, nfaces
= face
->num_faces
;
682 LOG_FONT (stderr
, " * caching font info for `%s'...\n", filename
);
684 file
= new FontFile (filename
, guid
);
685 file
->faces
= g_ptr_array_new ();
688 args
.flags
= FT_OPEN_STREAM
;
689 args
.stream
= stream
;
691 if (i
> 0 && FT_Open_Face (libft2
, &args
, i
, &face
) != 0)
694 fi
= new FaceInfo (file
, face
, i
);
695 g_ptr_array_add (file
->faces
, fi
);
699 font_stream_reset (stream
);
702 } while (i
< nfaces
);
704 fonts
->Append (file
);
708 font_index_destroy (gpointer user_data
)
710 delete ((FontIndex
*) user_data
);
718 FontFace::FontFace (FontManager
*manager
, FT_Face face
, char *key
)
720 FT_Set_Pixel_Sizes (face
, 0, (int) FONT_FACE_SIZE
);
721 this->cur_size
= FONT_FACE_SIZE
;
722 this->manager
= manager
;
727 g_hash_table_insert (manager
->faces
, key
, this);
730 FontFace::~FontFace ()
734 g_hash_table_steal (manager
->faces
, key
);
736 stream
= face
->stream
;
738 font_stream_destroy (stream
);
758 FontFace::GetFamilyName ()
760 return face
->family_name
;
764 FontFace::GetStyleName ()
766 return face
->style_name
;
770 FontFace::IsScalable ()
772 return FT_IS_SCALABLE (face
);
776 FontFace::IsItalic ()
778 return (face
->style_flags
& FT_STYLE_FLAG_ITALIC
);
784 return (face
->style_flags
& FT_STYLE_FLAG_BOLD
);
788 FontFace::GetCharFromIndex (guint32 index
)
796 unichar
= FT_Get_First_Char (face
, &idx
);
797 while (idx
!= index
&& idx
!= 0)
798 unichar
= FT_Get_Next_Char (face
, unichar
, &idx
);
807 FontFace::GetCharIndex (gunichar unichar
)
809 return FcFreeTypeCharIndex (face
, unichar
);
813 FontFace::HasChar (gunichar unichar
)
815 return FcFreeTypeCharIndex (face
, unichar
) != 0;
819 FontFace::GetExtents (double size
, FontFaceExtents
*extents
)
821 double scale
= size
/ face
->units_per_EM
;
823 if (FT_IS_SFNT (face
)) {
824 TT_HoriHeader
*hhea
= (TT_HoriHeader
*) FT_Get_Sfnt_Table (face
, ft_sfnt_hhea
);
825 TT_OS2
*os2
= (TT_OS2
*) FT_Get_Sfnt_Table (face
, ft_sfnt_os2
);
826 int height
, ascender
, descender
;
828 if (os2
&& (os2
->fsSelection
& fsSelectionUseTypoMetrics
)) {
829 // Use the typographic Ascender, Descender, and LineGap values for everything.
830 height
= os2
->sTypoAscender
- os2
->sTypoDescender
+ os2
->sTypoLineGap
;
831 descender
= -os2
->sTypoDescender
;
832 ascender
= os2
->sTypoAscender
;
834 // Calculate the LineSpacing for both the hhea table and the OS/2 table.
835 int hhea_height
= hhea
->Ascender
+ abs (hhea
->Descender
) + hhea
->Line_Gap
;
836 int os2_height
= os2
? (os2
->usWinAscent
+ os2
->usWinDescent
) : 0;
838 // The LineSpacing is the maximum of the two sumations.
839 height
= MAX (hhea_height
, os2_height
);
841 // If the OS/2 table exists, use usWinAscent as the
842 // ascender. Otherwise use hhea's Ascender value.
843 ascender
= os2
? os2
->usWinAscent
: hhea
->Ascender
;
845 // The Descender becomes the difference between the
846 // LineSpacing and the Ascender.
847 descender
= height
- ascender
;
850 extents
->descent
= -descender
* scale
;
851 extents
->ascent
= ascender
* scale
;
852 extents
->height
= height
* scale
;
854 // Fall back to the default FreeType2 values.
855 extents
->descent
= face
->descender
* scale
;
856 extents
->ascent
= face
->ascender
* scale
;
857 extents
->height
= face
->height
* scale
;
860 extents
->underline_thickness
= face
->underline_thickness
* scale
;
861 extents
->underline_position
= -face
->underline_position
* scale
;
862 extents
->underline_position
+= ((extents
->underline_thickness
+ 1) / 2.0);
864 if (extents
->underline_thickness
< 1.0)
865 extents
->underline_thickness
= 1.0;
869 FontFace::Kerning (double size
, guint32 left
, guint32 right
)
873 if (!FT_HAS_KERNING (face
) || left
== 0 || right
== 0)
876 if (size
<= FONT_FACE_SIZE
) {
877 if (cur_size
!= FONT_FACE_SIZE
) {
878 FT_Set_Pixel_Sizes (face
, 0, (int) FONT_FACE_SIZE
);
879 cur_size
= FONT_FACE_SIZE
;
882 FT_Get_Kerning (face
, left
, right
, FT_KERNING_DEFAULT
, &kerning
);
884 return (kerning
.x
* size
) / (FONT_FACE_SIZE
* 64.0);
886 if (cur_size
!= size
) {
887 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
891 FT_Get_Kerning (face
, left
, right
, FT_KERNING_DEFAULT
, &kerning
);
893 return kerning
.x
/ 64.0;
898 font_move_to (FT_Vector
*to
, void *user_data
)
900 moon_path
*path
= (moon_path
*) user_data
;
903 x
= DOUBLE_FROM_26_6 (to
->x
);
904 y
= DOUBLE_FROM_26_6 (to
->y
);
906 moon_move_to (path
, x
, y
);
912 font_line_to (FT_Vector
*to
, void *user_data
)
914 moon_path
*path
= (moon_path
*) user_data
;
917 x
= DOUBLE_FROM_26_6 (to
->x
);
918 y
= DOUBLE_FROM_26_6 (to
->y
);
920 moon_line_to (path
, x
, y
);
926 font_conic_to (FT_Vector
*control
, FT_Vector
*to
, void *user_data
)
928 moon_path
*path
= (moon_path
*) user_data
;
932 x
= DOUBLE_FROM_26_6 (control
->x
);
933 y
= DOUBLE_FROM_26_6 (control
->y
);
935 x3
= DOUBLE_FROM_26_6 (to
->x
);
936 y3
= DOUBLE_FROM_26_6 (to
->y
);
938 moon_quad_curve_to (path
, x
, y
, x3
, y3
);
944 font_cubic_to (FT_Vector
*control1
, FT_Vector
*control2
, FT_Vector
*to
, void *user_data
)
946 moon_path
*path
= (moon_path
*) user_data
;
951 x0
= DOUBLE_FROM_26_6 (control1
->x
);
952 y0
= DOUBLE_FROM_26_6 (control1
->y
);
954 x1
= DOUBLE_FROM_26_6 (control2
->x
);
955 y1
= DOUBLE_FROM_26_6 (control2
->y
);
957 x2
= DOUBLE_FROM_26_6 (to
->x
);
958 y2
= DOUBLE_FROM_26_6 (to
->y
);
960 moon_curve_to (path
, x0
, y0
, x1
, y1
, x2
, y2
);
965 static const FT_Outline_Funcs outline_funcs
= {
966 (FT_Outline_MoveToFunc
) font_move_to
,
967 (FT_Outline_LineToFunc
) font_line_to
,
968 (FT_Outline_ConicToFunc
) font_conic_to
,
969 (FT_Outline_CubicToFunc
) font_cubic_to
,
975 FontFace::LoadGlyph (double size
, GlyphInfo
*glyph
, StyleSimulations simulate
)
977 FT_Glyph_Metrics
*metrics
;
978 FT_Fixed hori_adj
= 0;
986 if (size
<= FONT_FACE_SIZE
) {
987 if (cur_size
!= FONT_FACE_SIZE
) {
988 FT_Set_Pixel_Sizes (face
, 0, (int) FONT_FACE_SIZE
);
989 cur_size
= FONT_FACE_SIZE
;
992 scale
= size
/ FONT_FACE_SIZE
;
994 if (cur_size
!= size
) {
995 FT_Set_Pixel_Sizes (face
, 0, (int) size
);
1002 if (FT_Load_Glyph (face
, glyph
->index
, LOAD_FLAGS
) != 0)
1005 if (FT_Render_Glyph (face
->glyph
, FT_RENDER_MODE_NORMAL
) != 0)
1008 // invert the glyph over the y-axis and scale
1009 matrix
.xx
= DOUBLE_TO_16_16 (scale
);
1012 matrix
.yy
= -DOUBLE_TO_16_16 (scale
);
1014 if ((simulate
& StyleSimulationsBold
) != 0) {
1015 FT_Outline_Embolden (&face
->glyph
->outline
, EMBOLDEN_STRENGTH_26_6
);
1016 hori_adj
= EMBOLDEN_STRENGTH_16_16
;
1017 bbox_adj
= EMBOLDEN_STRENGTH_26_6
;
1020 if ((simulate
& StyleSimulationsItalic
) != 0)
1021 FT_Matrix_Multiply (&italicize
, &matrix
);
1023 glyph
->path
= moon_path_new (8);
1024 FT_Outline_Transform (&face
->glyph
->outline
, &matrix
);
1025 FT_Outline_Decompose (&face
->glyph
->outline
, &outline_funcs
, glyph
->path
);
1027 metrics
= &face
->glyph
->metrics
;
1029 glyph
->metrics
.horiBearingX
= DOUBLE_FROM_26_6 (metrics
->horiBearingX
) * scale
;
1030 //glyph->metrics.horiBearingY = DOUBLE_FROM_26_6 (metrics->horiBearingY) * scale;
1031 // always prefer linearHoriAdvance over horiAdvance since the later is rounded to an integer
1032 glyph
->metrics
.horiAdvance
= DOUBLE_FROM_16_16 (face
->glyph
->linearHoriAdvance
+ hori_adj
) * scale
;
1033 //glyph->metrics.height = DOUBLE_FROM_26_6 (metrics->height + bbox_adj) * scale;
1034 //glyph->metrics.width = DOUBLE_FROM_26_6 (metrics->width + bbox_adj) * scale;
1040 font_face_destroy (gpointer data
)
1042 FontFace
*face
= (FontFace
*) data
;
1052 FontManager::FontManager ()
1056 resources
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, font_index_destroy
);
1057 faces
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, font_face_destroy
);
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 (resources
);
1074 g_hash_table_destroy (faces
);
1075 FT_Done_FreeType (libft2
);
1084 IndexFontSubdirectory (FT_Library libft2
, const char *name
, GString
*path
, FontIndex
**out
)
1086 FontIndex
*fontdir
= *out
;
1087 struct dirent
*dent
;
1096 if (!(dir
= opendir (path
->str
)))
1097 return fontdir
!= NULL
;
1099 LOG_FONT (stderr
, " * indexing font directory `%s'...\n", path
->str
);
1101 g_string_append_c (path
, G_DIR_SEPARATOR
);
1104 while ((dent
= readdir (dir
))) {
1105 if (!strcmp (dent
->d_name
, "..") ||
1106 !strcmp (dent
->d_name
, "."))
1109 g_string_append (path
, dent
->d_name
);
1111 if (stat (path
->str
, &st
) == -1)
1114 if (S_ISDIR (st
.st_mode
)) {
1115 IndexFontSubdirectory (libft2
, name
, path
, &fontdir
);
1119 if (!(stream
= font_stream_new (path
->str
, NULL
)))
1122 args
.flags
= FT_OPEN_STREAM
;
1123 args
.stream
= stream
;
1127 if (FT_Open_Face (libft2
, &args
, 0, &face
) != 0) {
1128 // not a valid font file... is it maybe an obfuscated font?
1129 if (!is_odttf (dent
->d_name
) || !font_stream_set_guid (stream
, dent
->d_name
)) {
1130 font_stream_destroy (stream
);
1134 font_stream_reset (stream
);
1136 args
.flags
= FT_OPEN_STREAM
;
1137 args
.stream
= stream
;
1139 if (FT_Open_Face (libft2
, &args
, 0, &face
) != 0) {
1140 font_stream_destroy (stream
);
1147 if (fontdir
== NULL
)
1148 fontdir
= new FontIndex (name
);
1151 fontdir
->CacheFontInfo (libft2
, path
->str
, stream
, face
, obfuscated
? dent
->d_name
: NULL
);
1153 font_stream_destroy (stream
);
1156 g_string_truncate (path
, len
);
1163 return fontdir
!= NULL
;
1167 IndexFontDirectory (FT_Library libft2
, const char *name
, const char *dirname
)
1169 FontIndex
*fontdir
= NULL
;
1173 path
= g_string_new (dirname
);
1176 if (!IndexFontSubdirectory (libft2
, name
, path
, &fontdir
)) {
1177 g_string_free (path
, true);
1181 g_string_truncate (path
, len
);
1182 fontdir
->path
= path
->str
;
1184 g_string_free (path
, false);
1190 IndexFontFile (FT_Library libft2
, const char *name
, const char *path
)
1192 const char *filename
= path_get_basename (name
);
1193 FontIndex
*index
= NULL
;
1199 LOG_FONT (stderr
, " * indexing font file `%s'...\n", path
);
1201 if (!(stream
= font_stream_new (path
, NULL
)))
1204 args
.flags
= FT_OPEN_STREAM
;
1205 args
.stream
= stream
;
1209 if (FT_Open_Face (libft2
, &args
, 0, &face
) != 0) {
1210 // not a valid font file... is it maybe an obfuscated font?
1211 if (!is_odttf (filename
) || !font_stream_set_guid (stream
, filename
)) {
1212 font_stream_destroy (stream
);
1216 font_stream_reset (stream
);
1218 args
.flags
= FT_OPEN_STREAM
;
1219 args
.stream
= stream
;
1221 if (FT_Open_Face (libft2
, &args
, 0, &face
) != 0) {
1222 font_stream_destroy (stream
);
1229 index
= new FontIndex (name
);
1230 index
->path
= g_strdup (path
);
1233 index
->CacheFontInfo (libft2
, path
, stream
, face
, obfuscated
? filename
: NULL
);
1235 font_stream_destroy (stream
);
1241 FontManager::AddResource (const char *resource
, const char *path
)
1246 LOG_FONT (stderr
, "Adding font resource '%s' at %s\n", resource
, path
);
1248 if ((index
= (FontIndex
*) g_hash_table_lookup (resources
, resource
)))
1251 if (stat (path
, &st
) == -1)
1254 if (S_ISDIR (st
.st_mode
))
1255 index
= IndexFontDirectory (libft2
, resource
, path
);
1256 else if (S_ISREG (st
.st_mode
))
1257 index
= IndexFontFile (libft2
, resource
, path
);
1262 g_hash_table_insert (resources
, index
->name
, index
);
1266 FontManager::AddResource (ManagedStreamCallbacks
*stream
)
1268 char buf
[4096], *resource
, *dirname
, *path
;
1273 if (!stream
->CanRead (stream
->handle
))
1276 if (!root
&& !(root
= CreateTempDir ("moonlight-fonts")))
1279 // check if we've already added this resource
1280 resource
= g_strdup_printf ("font-source://%p", stream
->handle
);
1281 if (g_hash_table_lookup (resources
, resource
) != NULL
)
1284 snprintf (buf
, sizeof (buf
), "%p", stream
->handle
);
1285 path
= g_build_filename (root
, buf
, NULL
);
1287 if ((fd
= open (path
, O_CREAT
| O_EXCL
| O_WRONLY
, 0600)) == -1) {
1293 // write the managed stream to disk
1294 pos
= stream
->Position (stream
->handle
);
1296 if (stream
->CanSeek (stream
->handle
))
1297 stream
->Seek (stream
->handle
, 0, SEEK_SET
);
1299 while ((nread
= stream
->Read (stream
->handle
, buf
, 0, sizeof (buf
))) > 0) {
1300 if (write_all (fd
, buf
, (size_t) nread
) == -1) {
1309 // reset the stream to the original state
1310 if (stream
->CanSeek (stream
->handle
) && pos
!= -1)
1311 stream
->Seek (stream
->handle
, pos
, SEEK_SET
);
1315 // check to see if the resource is zipped
1316 if ((zipfile
= unzOpen (path
))) {
1317 snprintf (buf
, sizeof (buf
), "%p.zip", stream
->handle
);
1318 dirname
= g_build_filename (root
, buf
, NULL
);
1320 // create a directory to contain our unzipped content
1321 if (mkdir (dirname
, 0700) == -1) {
1330 // unzip the contents
1331 if (!ExtractAll (zipfile
, dirname
, false)) {
1332 RemoveDir (dirname
);
1348 AddResource (resource
, path
);
1356 style_diff (FontStyleInfo
*actual
, FontStyleInfo
*desired
)
1359 // we convert to FontConfig for 2 reasons:
1360 // 1. negative values and values > 1023
1361 // 2. smaller ranges
1362 int weight
= abs (fc_weight (actual
->weight
) - fc_weight (desired
->weight
));
1364 if (actual
->slant
== desired
->slant
)
1367 if (actual
->slant
== FontStylesNormal
) {
1368 // we can emulate italic/oblique, but we would still prefer the real
1369 // italic font if we can find it so apply a slight penalty
1370 return 1000 + weight
;
1373 // ouch, apply a huge penalty
1374 return 1000000 + weight
;
1376 // convert to FontConfig values so that each style property fits within 8 bits
1377 int weight
= abs (fc_weight (actual
->weight
) - fc_weight (desired
->weight
));
1378 int width
= abs (fc_width (actual
->width
) - fc_width (desired
->width
));
1379 int slant
= abs (fc_slant (actual
->slant
) - fc_slant (desired
->slant
));
1381 // weight has the highest priority, followed by weight and then slant
1382 return ((width
& 0xff) << 16) | ((weight
& 0xff) << 8) | (slant
& 0xff);
1387 canon_font_family_and_style (FontStyleInfo
*desired
, const char *family
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1389 desired
->width
= FontStretchesNormal
;
1390 desired
->weight
= FontWeightsNormal
;
1391 desired
->slant
= FontStylesNormal
;
1392 desired
->family_name
= NULL
;
1395 // extract whatever little style info we can from the family name
1396 style_info_parse (family
, desired
, true);
1398 // override style with user-specified attributes
1399 if (!(desired
->set
& Width
))
1400 desired
->width
= stretch
;
1401 if (!(desired
->set
& Weight
))
1402 desired
->weight
= weight
;
1403 if (!(desired
->set
& Slant
))
1404 desired
->slant
= style
;
1408 IndexMatchFace (FontIndex
*index
, const char *family
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1410 FontFile
*file
= (FontFile
*) index
->fonts
->First ();
1411 FaceInfo
*face
, *best
= NULL
;
1412 FontStyleInfo desired
;
1413 int closest
= G_MAXINT
;
1417 LOG_FONT (stderr
, " * searching index for %s; %s\n", family
, style_info_to_string (stretch
, weight
, style
));
1419 canon_font_family_and_style (&desired
, family
, stretch
, weight
, style
);
1421 LOG_FONT (stderr
, " * canonicalized family/style: %s; %s\n", desired
.family_name
,
1422 style_info_to_string (desired
.width
, desired
.weight
, desired
.slant
));
1424 while (file
!= NULL
) {
1425 for (i
= 0; i
< file
->faces
->len
; i
++) {
1426 face
= (FaceInfo
*) file
->faces
->pdata
[i
];
1428 if (!g_ascii_strcasecmp (face
->family_name
, desired
.family_name
)) {
1429 diff
= style_diff (&face
->style
, &desired
);
1430 if (diff
< closest
) {
1437 file
= (FontFile
*) file
->next
;
1440 g_free (desired
.family_name
);
1446 FontManager::OpenFontFace (const char *filename
, const char *guid
, int index
)
1454 key
= g_strdup_printf ("%s#%d", filename
, index
);
1455 if ((ff
= (FontFace
*) g_hash_table_lookup (faces
, key
))) {
1461 if (!(stream
= font_stream_new (filename
, guid
))) {
1466 args
.flags
= FT_OPEN_STREAM
;
1467 args
.stream
= stream
;
1469 if (FT_Open_Face (libft2
, &args
, index
, &face
) != 0) {
1470 font_stream_destroy (stream
);
1475 if (!FT_IS_SCALABLE (face
)) {
1476 FT_Done_Face (face
);
1477 font_stream_destroy (stream
);
1482 return new FontFace (this, face
, key
);
1486 FontManager::OpenFontResource (const char *resource
, const char *family
, int idx
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1493 LOG_FONT (stderr
, "OpenFontResource (\"%s\", \"%s\", %d, %s)\n", resource
? resource
: "(null)",
1494 family
? family
: "(null)", idx
, style_info_to_string (stretch
, weight
, style
));
1496 if (!(index
= (FontIndex
*) g_hash_table_lookup (resources
, resource
))) {
1497 LOG_FONT (stderr
, " * error: no such resource\n");
1501 if (family
!= NULL
) {
1503 if (!(fi
= IndexMatchFace (index
, family
, stretch
, weight
, style
))) {
1504 LOG_FONT (stderr
, " * error: resource does not contain requested font\n");
1507 } else if (idx
>= 0) {
1509 if (!(file
= (FontFile
*) index
->fonts
->First ()) || file
->next
!= NULL
)
1512 if ((int) file
->faces
->len
<= idx
)
1515 fi
= (FaceInfo
*) file
->faces
->pdata
[idx
];
1517 // no family or index specified... error?
1521 if (!(face
= OpenFontFace (fi
->file
->path
, fi
->file
->guid
, fi
->index
)))
1524 LOG_FONT (stderr
, " * opened %s; %s\n", face
->GetFamilyName (), face
->GetStyleName ());
1530 FontManager::OpenSystemFont (const char *family
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1532 FcPattern
*pattern
, *matched
;
1533 FontStyleInfo desired
;
1539 for (int attempt
= 0; attempt
< 2; attempt
++) {
1541 desired
.family_name
= g_strdup (family
);
1542 desired
.width
= stretch
;
1543 desired
.weight
= weight
;
1544 desired
.slant
= style
;
1546 g_free (desired
.family_name
);
1547 canon_font_family_and_style (&desired
, family
, stretch
, weight
, style
);
1550 LOG_FONT (stderr
, "Attempting to load installed font: %s %s... ", desired
.family_name
,
1551 style_info_to_string (desired
.width
, desired
.weight
, desired
.slant
));
1553 pattern
= FcPatternCreate ();
1554 FcPatternAddDouble (pattern
, FC_DPI
, dpi
);
1555 FcPatternAddString (pattern
, FC_FAMILY
, (const FcChar8
*) desired
.family_name
);
1556 FcPatternAddInteger (pattern
, FC_WIDTH
, fc_width (desired
.width
));
1557 FcPatternAddInteger (pattern
, FC_WEIGHT
, fc_weight (desired
.weight
));
1558 FcPatternAddInteger (pattern
, FC_SLANT
, fc_slant (desired
.slant
));
1559 FcDefaultSubstitute (pattern
);
1561 if (!(matched
= FcFontMatch (NULL
, pattern
, &result
))) {
1562 LOG_FONT (stderr
, "no matches\n");
1563 FcPatternDestroy (pattern
);
1567 FcPatternDestroy (pattern
);
1569 if (FcPatternGetString (matched
, FC_FILE
, 0, &filename
) != FcResultMatch
) {
1570 LOG_FONT (stderr
, "no filename\n");
1571 FcPatternDestroy (matched
);
1575 if (FcPatternGetInteger (matched
, FC_INDEX
, 0, &index
) != FcResultMatch
) {
1576 LOG_FONT (stderr
, "no index\n");
1577 FcPatternDestroy (matched
);
1581 if ((face
= OpenFontFace ((const char *) filename
, NULL
, index
))) {
1582 if (!g_ascii_strcasecmp (face
->GetFamilyName (), desired
.family_name
)) {
1583 LOG_FONT (stderr
, "got %s %s\n", face
->GetFamilyName (), face
->GetStyleName ());
1584 g_free (desired
.family_name
);
1585 FcPatternDestroy (matched
);
1589 LOG_FONT (stderr
, "family mismatch\n");
1592 LOG_FONT (stderr
, "family not found\n");
1595 FcPatternDestroy (matched
);
1598 g_free (desired
.family_name
);
1604 FontManager::OpenFont (const char *name
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
1611 if ((family
= strchr (name
, '#'))) {
1612 char *resource
= g_strndup (name
, family
- name
);
1616 //if ((index = strtol (family, &end, 10)) >= 0 && index < G_MAXINT && *end == '\0')
1617 // face = OpenFontResource (resource, NULL, index, stretch, weight, style);
1619 face
= OpenFontResource (resource
, family
, -1, stretch
, weight
, style
);
1623 face
= OpenSystemFont (name
, stretch
, weight
, style
);
1630 FontManager::OpenFont (const char *name
, int index
)
1632 return OpenFontResource (name
, NULL
, index
, FontStretchesNormal
, FontWeightsNormal
, FontStylesNormal
);