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
, double size
)
33 this->simulate
= StyleSimulationsNone
;
34 this->n_faces
= n_faces
;
35 this->master
= master
;
44 TextFont::~TextFont ()
48 for (int i
= 0; i
< n_faces
; i
++)
54 TextFont::ClearGlyphCache ()
56 for (int i
= 0; i
< n_glyphs
; i
++) {
58 moon_path_destroy (glyphs
[i
].path
);
65 TextFont::UpdateFaceExtents ()
67 faces
[master
]->GetExtents (size
, &extents
);
71 TextFont::Load (const char *resource
, int index
, double size
, StyleSimulations simulate
)
73 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
77 faces
= g_new (FontFace
*, 1);
78 if (!(faces
[0] = manager
->OpenFont (resource
, index
))) {
83 font
= new TextFont (faces
, 1, 0, size
);
84 font
->simulate
= simulate
;
89 #define lowercase(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
92 strcase_equal (gconstpointer v
, gconstpointer v2
)
94 return g_ascii_strcasecmp ((const char *) v
, (const char *) v2
) == 0;
99 strcase_hash (gconstpointer key
)
101 const char *p
= (const char *) key
;
105 h
= (h
<< 5) - h
+ lowercase (*p
);
114 const char *families
[6];
115 } default_fonts
[] = {
116 { "", { "Lucida Sans Unicode", "Liberation Sans", "Bitstream Vera Sans", "DejaVu Sans", "Luxi Sans", NULL
} },
117 { "ja", { "MS Gothic", "Meiryo", "MS PMincho", "MS PGothic", "MS UI Gothic", NULL
} },
118 { "ko", { "Gulim", "Malgun Gothic", "Dotum", "Arial Unicode MS", "Batang", NULL
} },
119 { "zh", { "SimSun", "SimHei", "Microsoft YaHei", "Arial Unicode MS", NULL
, NULL
} },
122 static const char lang_table
[256] = {
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
126 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
127 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
128 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
129 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
130 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
134 canon_lang (const char *lang
)
136 const char *s
= lang
;
139 d
= canon
= (char *) g_malloc (strlen (lang
) + 1);
141 *d
++ = lang_table
[(unsigned char) *s
++];
150 langs_equal (const char *lang0
, const char *lang1
)
152 const unsigned char *p0
= (const unsigned char *) lang0
;
153 const unsigned char *p1
= (const unsigned char *) lang1
;
155 while (lang_table
[*p0
] && lang_table
[*p0
] == lang_table
[*p1
])
158 return lang_table
[*p0
] == lang_table
[*p1
];
163 langs_match (const char *pattern
, const char *actual
)
165 size_t n
= strlen (pattern
);
167 return strncmp (pattern
, actual
, n
) == 0 &&
168 (actual
[n
] == '\0' || actual
[n
] == '-');
172 LoadPortableUserInterface (FontManager
*manager
, GPtrArray
*faces
, const char *lang
, FontStretches stretch
, FontWeights weight
, FontStyles style
)
174 guint preferred
= G_N_ELEMENTS (default_fonts
);
175 const char **families
;
180 if (Deployment::GetCurrent ()->IsLoadedFromXap ()) {
181 // Silverlight 2.0 applications default to Verdana instead of Lucida Sans Unicode
182 if ((face
= manager
->OpenFont ("Verdana", stretch
, weight
, style
)))
183 g_ptr_array_add (faces
, face
);
186 // Load Lucida Sans and save the index because we use it later for face extents
187 families
= default_fonts
[0].families
;
190 for (j
= 0; families
[j
]; j
++) {
191 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
192 g_ptr_array_add (faces
, face
);
198 // use the xml:lang tag to load the preferred default font first
199 for (i
= 1; i
< G_N_ELEMENTS (default_fonts
); i
++) {
200 if (langs_match (default_fonts
[i
].lang
, lang
)) {
201 families
= default_fonts
[i
].families
;
203 for (j
= 0; families
[j
]; j
++) {
204 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
205 g_ptr_array_add (faces
, face
);
216 // Now load the remaining default font faces...
217 for (i
= 1; i
< G_N_ELEMENTS (default_fonts
); i
++) {
218 // avoid re-loading the preferred font face
222 families
= default_fonts
[i
].families
;
223 for (j
= 0; families
[j
]; j
++) {
224 if ((face
= manager
->OpenFont (families
[j
], stretch
, weight
, style
))) {
225 g_ptr_array_add (faces
, face
);
235 TextFont::Load (const TextFontDescription
*desc
)
237 FontManager
*manager
= Deployment::GetCurrent ()->GetFontManager ();
238 FontStretches stretch
= desc
->GetStretch ();
239 FontWeights weight
= desc
->GetWeight ();
240 const char *source
= desc
->GetSource ();
241 const char *lang
= desc
->GetLanguage ();
242 char **families
= desc
->GetFamilies ();
243 FontStyles style
= desc
->GetStyle ();
244 int lucida
, master
= -1;
252 loaded
= g_hash_table_new (strcase_hash
, strcase_equal
);
254 faces
= g_ptr_array_new ();
257 for (i
= 0; families
[i
]; i
++) {
258 if (g_hash_table_lookup (loaded
, families
[i
]))
261 if (!g_ascii_strcasecmp (families
[i
], "Portable User Interface")) {
262 lucida
= LoadPortableUserInterface (manager
, faces
, lang
, stretch
, weight
, style
);
268 if (source
&& !strchr (families
[i
], '#')) {
269 // if there is a font source, try loading from the font source first
270 name
= g_strdup_printf ("%s#%s", source
, families
[i
]);
271 face
= manager
->OpenFont (name
, stretch
, weight
, style
);
276 face
= manager
->OpenFont (families
[i
], stretch
, weight
, style
);
282 g_ptr_array_add (faces
, face
);
286 g_hash_table_insert (loaded
, families
[i
], GINT_TO_POINTER (true));
289 if ((face
= manager
->OpenFont (source
, 0))) {
290 g_ptr_array_add (faces
, face
);
295 // always add PUI as fallback unless already added
296 if (!g_hash_table_lookup (loaded
, "Portable User Interface")) {
297 lucida
= LoadPortableUserInterface (manager
, faces
, lang
, stretch
, weight
, style
);
302 g_hash_table_destroy (loaded
);
303 g_strfreev (families
);
305 if (faces
->len
== 0) {
306 g_ptr_array_free (faces
, true);
310 font
= new TextFont ((FontFace
**) faces
->pdata
, faces
->len
, master
, desc
->GetSize ());
311 g_ptr_array_free (faces
, false);
318 TextFont::SetSize (double size
)
320 if (this->size
== size
)
325 UpdateFaceExtents ();
332 TextFont::GetSize () const
338 TextFont::SetStyleSimulations (StyleSimulations simulate
)
340 if (this->simulate
== simulate
)
343 this->simulate
= simulate
;
351 TextFont::GetStyleSimulations () const
357 TextFont::Kerning (GlyphInfo
*left
, GlyphInfo
*right
)
359 #ifdef ENABLE_KERNING
360 if (left
->face
!= right
->face
)
363 return left
->face
->Kerning (size
, left
->index
, right
->index
);
370 TextFont::Descender () const
372 return extents
.descent
;
376 TextFont::Ascender () const
378 return extents
.ascent
;
382 TextFont::Height () const
384 return extents
.height
;
388 glyphsort (const void *v1
, const void *v2
)
390 GlyphInfo
*g1
= (GlyphInfo
*) v1
;
391 GlyphInfo
*g2
= (GlyphInfo
*) v2
;
392 gint64 cmp
= g2
->atime
- g1
->atime
;
394 if ((cmp
= g2
->atime
- g1
->atime
) < 0)
397 return cmp
> 0 ? 1 : 0;
401 TextFont::GetGlyphInfo (FontFace
*face
, gunichar unichar
, guint32 index
)
403 gint64 now
= get_now ();
404 GlyphInfo glyph
, *slot
;
407 for (i
= 0; i
< n_glyphs
; i
++) {
408 if (glyphs
[i
].unichar
== unichar
) {
415 glyph
.unichar
= unichar
;
422 // figure out what to simulate
423 simulate
= StyleSimulationsNone
;
424 if (FontWeightIsBold (desc
->GetWeight ()) && !face
->IsBold ())
425 simulate
= (StyleSimulations
) (simulate
| StyleSimulationsBold
);
426 if (desc
->GetStyle () == FontStylesItalic
&& !face
->IsItalic ())
427 simulate
= (StyleSimulations
) (simulate
| StyleSimulationsItalic
);
430 if (!face
->LoadGlyph (size
, &glyph
, simulate
))
433 if (n_glyphs
== GLYPH_CACHE_SIZE
) {
434 // need to expire the least recently requested glyph (which will be the last element in the array after sorting)
435 qsort (glyphs
, n_glyphs
, sizeof (GlyphInfo
), glyphsort
);
437 for (i
= 0; i
< n_glyphs
; i
++)
438 fprintf (stderr
, "glyphs[%d].atime = %" G_GINT64_FORMAT
"\n", i
, glyphs
[i
].atime
);
440 slot
= &glyphs
[n_glyphs
- 1];
443 moon_path_destroy (slot
->path
);
445 slot
= &glyphs
[n_glyphs
++];
448 memcpy (slot
, &glyph
, sizeof (GlyphInfo
));
453 //static GlyphInfo ZeroWidthNoBreakSpace = {
454 // 0xFEFF, 0, { 0.0, 0.0, 0.0, 0.0, 0.0 }, NULL, 0, 0
458 TextFont::GetGlyphInfo (gunichar unichar
)
460 FontFace
*face
= NULL
;
464 //if (unichar == 0xFEFF)
465 // return &ZeroWidthNoBreakSpace;
467 // find the face that contains this character
468 for (i
= 0; i
< n_faces
; i
++) {
469 if ((index
= faces
[i
]->GetCharIndex (unichar
)) != 0) {
476 // draw the empty glyph from the primary face
481 return GetGlyphInfo (face
, unichar
, index
);
485 TextFont::GetGlyphInfoByIndex (guint32 index
)
489 unichar
= faces
[0]->GetCharFromIndex (index
);
491 return GetGlyphInfo (faces
[0], unichar
, index
);
495 TextFont::UnderlinePosition () const
497 return extents
.underline_position
;
501 TextFont::UnderlineThickness () const
503 return extents
.underline_thickness
;
507 TextFont::Path (cairo_t
*cr
, GlyphInfo
*glyph
, double x
, double y
)
509 if (!glyph
->path
|| !(&glyph
->path
->cairo
)->data
)
512 cairo_translate (cr
, x
, y
);
513 cairo_append_path (cr
, &glyph
->path
->cairo
);
514 cairo_translate (cr
, -x
, -y
);
518 TextFont::Path (cairo_t
*cr
, gunichar unichar
, double x
, double y
)
522 if (!(glyph
= GetGlyphInfo (unichar
)))
525 Path (cr
, glyph
, x
, y
);
529 moon_append_path_with_origin (moon_path
*mpath
, cairo_path_t
*path
, double x
, double y
)
531 cairo_path_data_t
*data
;
533 moon_move_to (mpath
, x
, y
);
535 for (int i
= 0; i
< path
->num_data
; i
+= path
->data
[i
].header
.length
) {
536 data
= &path
->data
[i
];
538 switch (data
->header
.type
) {
539 case CAIRO_PATH_MOVE_TO
:
540 moon_move_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
);
542 case CAIRO_PATH_LINE_TO
:
543 moon_line_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
);
545 case CAIRO_PATH_CURVE_TO
:
546 moon_curve_to (mpath
, data
[1].point
.x
+ x
, data
[1].point
.y
+ y
,
547 data
[2].point
.x
+ x
, data
[2].point
.y
+ y
,
548 data
[3].point
.x
+ x
, data
[3].point
.y
+ y
);
550 case CAIRO_PATH_CLOSE_PATH
:
557 TextFont::AppendPath (moon_path
*path
, GlyphInfo
*glyph
, double x
, double y
)
559 if (!glyph
->path
|| !(&glyph
->path
->cairo
)->data
)
562 moon_append_path_with_origin (path
, &glyph
->path
->cairo
, x
, y
);
566 TextFont::AppendPath (moon_path
*path
, gunichar unichar
, double x
, double y
)
570 if (!(glyph
= GetGlyphInfo (unichar
)))
573 AppendPath (path
, glyph
, x
, y
);
577 TextFontDescription::TextFontDescription ()
586 style
= FontStylesNormal
;
587 weight
= FontWeightsNormal
;
588 stretch
= FontStretchesNormal
;
589 size
= 14.666666984558105;
592 TextFontDescription::~TextFontDescription ()
601 TextFontDescription::Reload ()
603 // load the new font first - trick to hit the cache for old FontFaces
604 TextFont
*ttf
= TextFont::Load (this);
611 TextFontDescription::GetFont ()
620 TextFontDescription::GetSource () const
626 TextFontDescription::SetSource (const char *source
)
631 if (!this->source
|| g_ascii_strcasecmp (this->source
, source
) != 0) {
632 g_free (this->source
);
633 this->source
= g_strdup (source
);
634 this->changed
= true;
641 g_free (this->source
);
643 this->changed
= true;
654 TextFontDescription::GetFamilies () const
661 if ((families
= g_strsplit (family
, ",", -1))) {
662 for (int i
= 0; families
[i
]; i
++)
663 g_strstrip (families
[i
]);
670 TextFontDescription::GetFamily () const
676 TextFontDescription::SetFamily (const char *family
)
681 if (!this->family
|| g_ascii_strcasecmp (this->family
, family
) != 0) {
682 g_free (this->family
);
683 this->family
= g_strdup (family
);
684 this->changed
= true;
691 g_free (this->family
);
693 this->changed
= true;
704 TextFontDescription::GetLanguage () const
710 TextFontDescription::SetLanguage (const char *lang
)
716 canon
= canon_lang (lang
);
718 if (!this->language
|| g_ascii_strcasecmp (this->language
, canon
) != 0) {
719 g_free (this->language
);
720 this->language
= canon
;
721 this->changed
= true;
728 if (this->language
) {
729 g_free (this->language
);
730 this->language
= NULL
;
731 this->changed
= true;
742 TextFontDescription::GetStyle () const
748 TextFontDescription::SetStyle (FontStyles style
)
750 bool changed
= this->style
!= style
;
754 this->changed
= true;
761 TextFontDescription::GetWeight () const
767 TextFontDescription::SetWeight (FontWeights weight
)
769 bool changed
= this->weight
!= weight
;
772 this->weight
= weight
;
773 this->changed
= true;
780 TextFontDescription::GetStretch () const
786 TextFontDescription::SetStretch (FontStretches stretch
)
788 bool changed
= this->stretch
!= stretch
;
791 this->stretch
= stretch
;
792 this->changed
= true;
799 TextFontDescription::GetSize () const
805 TextFontDescription::SetSize (double size
)
807 bool changed
= this->size
!= size
;
810 font
->SetSize (size
);