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>
22 #include "timesource.h"
23 #include "moon-path.h"
31 TextFont::TextFont (FontFace
**faces
, int n_faces
, int master
, bool gapless
, double size
)
33 this->simulate
= StyleSimulationsNone
;
34 this->n_faces
= n_faces
;
35 this->gapless
= gapless
;
36 this->master
= master
;
45 TextFont::~TextFont ()
49 for (int i
= 0; i
< n_faces
; i
++)
55 TextFont::ClearGlyphCache ()
57 for (int i
= 0; i
< n_glyphs
; i
++) {
59 moon_path_destroy (glyphs
[i
].path
);
66 TextFont::UpdateFaceExtents ()
68 faces
[master
]->GetExtents (size
, gapless
, &extents
);
72 TextFont::Load (const char *resource
, int index
, double size
, StyleSimulations simulate
)
74 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
78 faces
= g_new (FontFace
*, 1);
79 if (!(faces
[0] = manager
->OpenFont (resource
, index
))) {
84 font
= new TextFont (faces
, 1, 0, false, size
);
85 font
->simulate
= simulate
;
90 #define lowercase(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
93 strcase_equal (gconstpointer v
, gconstpointer v2
)
95 return g_ascii_strcasecmp ((const char *) v
, (const char *) v2
) == 0;
100 strcase_hash (gconstpointer key
)
102 const char *p
= (const char *) key
;
106 h
= (h
<< 5) - h
+ lowercase (*p
);
115 const char *families
[6];
116 } default_fonts
[] = {
117 { "", { "Lucida Sans Unicode", "Liberation Sans", "Bitstream Vera Sans", "DejaVu Sans", "Luxi Sans", NULL
} },
118 { "ja", { "MS Gothic", "Meiryo", "MS PMincho", "MS PGothic", "MS UI Gothic", NULL
} },
119 { "ko", { "Gulim", "Malgun Gothic", "Dotum", "Arial Unicode MS", "Batang", NULL
} },
120 { "zh", { "SimSun", "SimHei", "Microsoft YaHei", "Arial Unicode MS", NULL
, NULL
} },
123 static const char lang_table
[256] = {
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
127 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
128 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
129 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
130 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
131 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
135 canon_lang (const char *lang
)
137 const char *s
= lang
;
140 d
= canon
= (char *) g_malloc (strlen (lang
) + 1);
142 *d
++ = lang_table
[(unsigned char) *s
++];
151 langs_equal (const char *lang0
, const char *lang1
)
153 const unsigned char *p0
= (const unsigned char *) lang0
;
154 const unsigned char *p1
= (const unsigned char *) lang1
;
156 while (lang_table
[*p0
] && lang_table
[*p0
] == lang_table
[*p1
])
159 return lang_table
[*p0
] == lang_table
[*p1
];
164 langs_match (const char *pattern
, const char *actual
)
166 size_t n
= strlen (pattern
);
168 return strncmp (pattern
, actual
, n
) == 0 &&
169 (actual
[n
] == '\0' || actual
[n
] == '-');
173 LoadPortableUserInterface (FontManager
*manager
, GPtrArray
*faces
, const char *lang
, FontStretches stretch
, FontWeights weight
, FontStyles style
, bool *gapless
)
175 guint preferred
= G_N_ELEMENTS (default_fonts
);
176 bool first_font
= faces
->len
== 0;
177 bool silverlight_2_0
= false;
178 const char **families
;
183 // Check for Silverlight >= 2.0
184 if (Deployment::GetCurrent ()->IsLoadedFromXap ()) {
185 // Verdana is the first fallback in Silverlight >= 2.0 applications
186 if ((face
= manager
->OpenFont ("Verdana", stretch
, weight
, style
)))
187 g_ptr_array_add (faces
, face
);
189 silverlight_2_0
= true;
192 // Load Lucida Sans Unicode and save the index because we use it later for face extents
193 families
= default_fonts
[0].families
;
196 for (j
= 0; families
[j
]; j
++) {
197 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
198 // Note: Silverlight >= 2.0 seems to use Lucida Sans Unicode's height
199 // metrics minus the sTypoLineGap to represent the height metrics for
200 // the Portable User Interface collection of fonts. Silverlight 1.0,
201 // however, uses the Lucida Sans Unicode height metrics as-is.
203 // Only subtract the sTypoLineGap value IFF we managed to load the real
204 // Lucida Sans Unicode font, not if we had to fallback to one of the
205 // others (because they have much smaller metrics).
206 *gapless
= silverlight_2_0
&& j
== 0 && first_font
;
208 g_ptr_array_add (faces
, face
);
214 // use the xml:lang tag to load the preferred font for the language as the next fallback
215 for (i
= 1; i
< G_N_ELEMENTS (default_fonts
); i
++) {
216 if (langs_match (default_fonts
[i
].lang
, lang
)) {
217 families
= default_fonts
[i
].families
;
219 for (j
= 0; families
[j
]; j
++) {
220 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
221 g_ptr_array_add (faces
, face
);
232 // Now load the remaining default font faces...
233 for (i
= 1; i
< G_N_ELEMENTS (default_fonts
); i
++) {
234 // avoid re-loading the preferred font face
238 families
= default_fonts
[i
].families
;
239 for (j
= 0; families
[j
]; j
++) {
240 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
241 g_ptr_array_add (faces
, face
);
251 TextFont::Load (const TextFontDescription
*desc
)
253 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
254 FontStretches stretch
= desc
->GetStretch ();
255 FontWeights weight
= desc
->GetWeight ();
256 const char *source
= desc
->GetSource ();
257 const char *lang
= desc
->GetLanguage ();
258 char **families
= desc
->GetFamilies ();
259 FontStyles style
= desc
->GetStyle ();
260 int lucida
, master
= -1;
261 bool gapless
= false;
269 loaded
= g_hash_table_new (strcase_hash
, strcase_equal
);
271 faces
= g_ptr_array_new ();
274 for (i
= 0; families
[i
]; i
++) {
275 if (g_hash_table_lookup (loaded
, families
[i
]))
278 if (!g_ascii_strcasecmp (families
[i
], "Portable User Interface")) {
279 lucida
= LoadPortableUserInterface (manager
, faces
, lang
, stretch
, weight
, style
, &gapless
);
286 if (source
&& !strchr (families
[i
], '#')) {
287 // if there is a font source, try loading from the font source first
288 name
= g_strdup_printf ("%s#%s", source
, families
[i
]);
289 face
= manager
->OpenFont (name
, stretch
, weight
, style
);
294 face
= manager
->OpenFont (families
[i
], stretch
, weight
, style
);
297 g_ptr_array_add (faces
, face
);
304 g_hash_table_insert (loaded
, families
[i
], GINT_TO_POINTER (true));
307 if ((face
= manager
->OpenFont (source
, 0))) {
308 g_ptr_array_add (faces
, face
);
313 // always add PUI as fallback unless already added
314 if (!g_hash_table_lookup (loaded
, "Portable User Interface")) {
315 lucida
= LoadPortableUserInterface (manager
, faces
, lang
, stretch
, weight
, style
, &gapless
);
321 g_hash_table_destroy (loaded
);
322 g_strfreev (families
);
324 if (faces
->len
== 0) {
325 g_ptr_array_free (faces
, true);
329 font
= new TextFont ((FontFace
**) faces
->pdata
, faces
->len
, master
, gapless
, desc
->GetSize ());
330 g_ptr_array_free (faces
, false);
337 TextFont::SetSize (double size
)
339 if (this->size
== size
)
344 UpdateFaceExtents ();
351 TextFont::GetSize () const
357 TextFont::SetStyleSimulations (StyleSimulations simulate
)
359 if (this->simulate
== simulate
)
362 this->simulate
= simulate
;
370 TextFont::GetStyleSimulations () const
376 TextFont::Kerning (GlyphInfo
*left
, GlyphInfo
*right
)
378 #ifdef ENABLE_KERNING
379 if (left
->face
!= right
->face
)
382 return left
->face
->Kerning (size
, left
->index
, right
->index
);
389 TextFont::Descender () const
391 return extents
.descent
;
395 TextFont::Ascender () const
397 return extents
.ascent
;
401 TextFont::Height () const
403 return extents
.height
;
407 glyphsort (const void *v1
, const void *v2
)
409 GlyphInfo
*g1
= (GlyphInfo
*) v1
;
410 GlyphInfo
*g2
= (GlyphInfo
*) v2
;
411 gint64 cmp
= g2
->atime
- g1
->atime
;
413 if ((cmp
= g2
->atime
- g1
->atime
) < 0)
416 return cmp
> 0 ? 1 : 0;
420 TextFont::GetGlyphInfo (FontFace
*face
, gunichar unichar
, guint32 index
)
422 gint64 now
= get_now ();
423 GlyphInfo glyph
, *slot
;
426 for (i
= 0; i
< n_glyphs
; i
++) {
427 if (glyphs
[i
].unichar
== unichar
) {
434 glyph
.unichar
= unichar
;
441 // figure out what to simulate
442 simulate
= StyleSimulationsNone
;
443 if (FontWeightIsBold (desc
->GetWeight ()) && !face
->IsBold ())
444 simulate
= (StyleSimulations
) (simulate
| StyleSimulationsBold
);
445 if (desc
->GetStyle () == FontStylesItalic
&& !face
->IsItalic ())
446 simulate
= (StyleSimulations
) (simulate
| StyleSimulationsItalic
);
449 if (!face
->LoadGlyph (size
, &glyph
, simulate
))
452 if (n_glyphs
== GLYPH_CACHE_SIZE
) {
453 // need to expire the least recently requested glyph (which will be the last element in the array after sorting)
454 qsort (glyphs
, n_glyphs
, sizeof (GlyphInfo
), glyphsort
);
456 for (i
= 0; i
< n_glyphs
; i
++)
457 fprintf (stderr
, "glyphs[%d].atime = %" G_GINT64_FORMAT
"\n", i
, glyphs
[i
].atime
);
459 slot
= &glyphs
[n_glyphs
- 1];
462 moon_path_destroy (slot
->path
);
464 slot
= &glyphs
[n_glyphs
++];
467 memcpy (slot
, &glyph
, sizeof (GlyphInfo
));
472 //static GlyphInfo ZeroWidthNoBreakSpace = {
473 // 0xFEFF, 0, { 0.0, 0.0, 0.0, 0.0, 0.0 }, NULL, 0, 0
477 TextFont::GetGlyphInfo (gunichar unichar
)
479 FontFace
*face
= NULL
;
483 //if (unichar == 0xFEFF)
484 // return &ZeroWidthNoBreakSpace;
486 // find the face that contains this character
487 for (i
= 0; i
< n_faces
; i
++) {
488 if ((index
= faces
[i
]->GetCharIndex (unichar
)) != 0) {
495 // draw the empty glyph from the primary face
500 return GetGlyphInfo (face
, unichar
, index
);
504 TextFont::GetGlyphInfoByIndex (guint32 index
)
508 unichar
= faces
[0]->GetCharFromIndex (index
);
510 return GetGlyphInfo (faces
[0], unichar
, index
);
514 TextFont::UnderlinePosition () const
516 return extents
.underline_position
;
520 TextFont::UnderlineThickness () const
522 return extents
.underline_thickness
;
526 TextFont::Path (cairo_t
*cr
, GlyphInfo
*glyph
, double x
, double y
)
528 if (!glyph
->path
|| !(&glyph
->path
->cairo
)->data
)
531 cairo_translate (cr
, x
, y
);
532 cairo_append_path (cr
, &glyph
->path
->cairo
);
533 cairo_translate (cr
, -x
, -y
);
537 TextFont::Path (cairo_t
*cr
, gunichar unichar
, double x
, double y
)
541 if (!(glyph
= GetGlyphInfo (unichar
)))
544 Path (cr
, glyph
, x
, y
);
548 moon_append_path_with_origin (moon_path
*mpath
, cairo_path_t
*path
, double x
, double y
)
550 cairo_path_data_t
*data
;
552 moon_move_to (mpath
, x
, y
);
554 for (int i
= 0; i
< path
->num_data
; i
+= path
->data
[i
].header
.length
) {
555 data
= &path
->data
[i
];
557 switch (data
->header
.type
) {
558 case CAIRO_PATH_MOVE_TO
:
559 moon_move_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
);
561 case CAIRO_PATH_LINE_TO
:
562 moon_line_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
);
564 case CAIRO_PATH_CURVE_TO
:
565 moon_curve_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
,
566 data
[2].point
.x
+ x
, data
[2].point
.y
+ y
,
567 data
[3].point
.x
+ x
, data
[3].point
.y
+ y
);
569 case CAIRO_PATH_CLOSE_PATH
:
576 TextFont::AppendPath (moon_path
*path
, GlyphInfo
*glyph
, double x
, double y
)
578 if (!glyph
->path
|| !(&glyph
->path
->cairo
)->data
)
581 moon_append_path_with_origin (path
, &glyph
->path
->cairo
, x
, y
);
585 TextFont::AppendPath (moon_path
*path
, gunichar unichar
, double x
, double y
)
589 if (!(glyph
= GetGlyphInfo (unichar
)))
592 AppendPath (path
, glyph
, x
, y
);
596 TextFontDescription::TextFontDescription ()
605 style
= FontStylesNormal
;
606 weight
= FontWeightsNormal
;
607 stretch
= FontStretchesNormal
;
608 size
= 14.666666984558105;
611 TextFontDescription::~TextFontDescription ()
620 TextFontDescription::Reload ()
622 // load the new font first - trick to hit the cache for old FontFaces
623 TextFont
*ttf
= TextFont::Load (this);
630 TextFontDescription::GetFont ()
639 TextFontDescription::GetSource () const
645 TextFontDescription::SetSource (const char *source
)
650 if (!this->source
|| g_ascii_strcasecmp (this->source
, source
) != 0) {
651 g_free (this->source
);
652 this->source
= g_strdup (source
);
653 this->changed
= true;
660 g_free (this->source
);
662 this->changed
= true;
673 TextFontDescription::GetFamilies () const
680 if ((families
= g_strsplit (family
, ",", -1))) {
681 for (int i
= 0; families
[i
]; i
++)
682 g_strstrip (families
[i
]);
689 TextFontDescription::GetFamily () const
695 TextFontDescription::SetFamily (const char *family
)
700 if (!this->family
|| g_ascii_strcasecmp (this->family
, family
) != 0) {
701 g_free (this->family
);
702 this->family
= g_strdup (family
);
703 this->changed
= true;
710 g_free (this->family
);
712 this->changed
= true;
723 TextFontDescription::GetLanguage () const
729 TextFontDescription::SetLanguage (const char *lang
)
735 canon
= canon_lang (lang
);
737 if (!this->language
|| g_ascii_strcasecmp (this->language
, canon
) != 0) {
738 g_free (this->language
);
739 this->language
= canon
;
740 this->changed
= true;
747 if (this->language
) {
748 g_free (this->language
);
749 this->language
= NULL
;
750 this->changed
= true;
761 TextFontDescription::GetStyle () const
767 TextFontDescription::SetStyle (FontStyles style
)
769 bool changed
= this->style
!= style
;
773 this->changed
= true;
780 TextFontDescription::GetWeight () const
786 TextFontDescription::SetWeight (FontWeights weight
)
788 bool changed
= this->weight
!= weight
;
791 this->weight
= weight
;
792 this->changed
= true;
799 TextFontDescription::GetStretch () const
805 TextFontDescription::SetStretch (FontStretches stretch
)
807 bool changed
= this->stretch
!= stretch
;
810 this->stretch
= stretch
;
811 this->changed
= true;
818 TextFontDescription::GetSize () const
824 TextFontDescription::SetSize (double size
)
826 bool changed
= this->size
!= size
;
829 font
->SetSize (size
);