2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / fonts.cpp
blob53a3d9543a3fdf86e6ef6075f19bb9c343463190
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * fonts.cpp:
5 * Contact:
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.
13 #include <config.h>
15 #include <glib.h>
16 #include <glib/gstdio.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <math.h>
22 #include "timesource.h"
23 #include "moon-path.h"
24 #include "debug.h"
25 #include "fonts.h"
28 // TextFont
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;
36 this->faces = faces;
37 this->n_glyphs = 0;
38 this->size = size;
39 this->desc = NULL;
41 UpdateFaceExtents ();
44 TextFont::~TextFont ()
46 ClearGlyphCache ();
48 for (int i = 0; i < n_faces; i++)
49 faces[i]->unref ();
50 g_free (faces);
53 void
54 TextFont::ClearGlyphCache ()
56 for (int i = 0; i < n_glyphs; i++) {
57 if (glyphs[i].path)
58 moon_path_destroy (glyphs[i].path);
61 n_glyphs = 0;
64 void
65 TextFont::UpdateFaceExtents ()
67 faces[master]->GetExtents (size, &extents);
70 TextFont *
71 TextFont::Load (const char *resource, int index, double size, StyleSimulations simulate)
73 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
74 FontFace **faces;
75 TextFont *font;
77 faces = g_new (FontFace *, 1);
78 if (!(faces[0] = manager->OpenFont (resource, index))) {
79 g_free (faces);
80 return NULL;
83 font = new TextFont (faces, 1, 0, size);
84 font->simulate = simulate;
86 return font;
89 #define lowercase(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
91 static int
92 strcase_equal (gconstpointer v, gconstpointer v2)
94 return g_ascii_strcasecmp ((const char *) v, (const char *) v2) == 0;
98 static guint
99 strcase_hash (gconstpointer key)
101 const char *p = (const char *) key;
102 guint h = 0;
104 while (*p != '\0') {
105 h = (h << 5) - h + lowercase (*p);
106 p++;
109 return h;
112 static struct {
113 const char *lang;
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
133 static char *
134 canon_lang (const char *lang)
136 const char *s = lang;
137 char *canon, *d;
139 d = canon = (char *) g_malloc (strlen (lang) + 1);
140 while (*s != '\0')
141 *d++ = lang_table[(unsigned char) *s++];
143 *d = '\0';
145 return canon;
148 #if 0
149 static bool
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])
156 p0++, p1++;
158 return lang_table[*p0] == lang_table[*p1];
160 #endif
162 static bool
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] == '-');
171 static int
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;
176 FontFace *face;
177 guint lucida;
178 guint i, j;
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;
188 lucida = faces->len;
190 for (j = 0; families[j]; j++) {
191 if ((face = manager->OpenFont (families[j], stretch, weight, style))) {
192 g_ptr_array_add (faces, face);
193 break;
197 if (lang != NULL) {
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);
206 break;
210 preferred = i;
211 break;
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
219 if (i == preferred)
220 continue;
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);
226 break;
231 return (int) lucida;
234 TextFont *
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;
245 GHashTable *loaded;
246 GPtrArray *faces;
247 FontFace *face;
248 TextFont *font;
249 char *name;
250 int i;
252 loaded = g_hash_table_new (strcase_hash, strcase_equal);
254 faces = g_ptr_array_new ();
256 if (families) {
257 for (i = 0; families[i]; i++) {
258 if (g_hash_table_lookup (loaded, families[i]))
259 continue;
261 if (!g_ascii_strcasecmp (families[i], "Portable User Interface")) {
262 lucida = LoadPortableUserInterface (manager, faces, lang, stretch, weight, style);
263 if (master == -1)
264 master = lucida;
265 } else {
266 face = NULL;
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);
272 g_free (name);
275 if (face == NULL)
276 face = manager->OpenFont (families[i], stretch, weight, style);
278 if (face != NULL) {
279 if (master == -1)
280 master = 0;
282 g_ptr_array_add (faces, face);
286 g_hash_table_insert (loaded, families[i], GINT_TO_POINTER (true));
288 } else if (source) {
289 if ((face = manager->OpenFont (source, 0))) {
290 g_ptr_array_add (faces, face);
291 master = 0;
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);
298 if (master == -1)
299 master = lucida;
302 g_hash_table_destroy (loaded);
303 g_strfreev (families);
305 if (faces->len == 0) {
306 g_ptr_array_free (faces, true);
307 return NULL;
310 font = new TextFont ((FontFace **) faces->pdata, faces->len, master, desc->GetSize ());
311 g_ptr_array_free (faces, false);
312 font->desc = desc;
314 return font;
317 bool
318 TextFont::SetSize (double size)
320 if (this->size == size)
321 return false;
323 this->size = size;
325 UpdateFaceExtents ();
326 ClearGlyphCache ();
328 return true;
331 double
332 TextFont::GetSize () const
334 return size;
337 bool
338 TextFont::SetStyleSimulations (StyleSimulations simulate)
340 if (this->simulate == simulate)
341 return false;
343 this->simulate = simulate;
345 ClearGlyphCache ();
347 return true;
350 StyleSimulations
351 TextFont::GetStyleSimulations () const
353 return simulate;
356 double
357 TextFont::Kerning (GlyphInfo *left, GlyphInfo *right)
359 #ifdef ENABLE_KERNING
360 if (left->face != right->face)
361 return 0.0;
363 return left->face->Kerning (size, left->index, right->index);
364 #else
365 return 0.0;
366 #endif
369 double
370 TextFont::Descender () const
372 return extents.descent;
375 double
376 TextFont::Ascender () const
378 return extents.ascent;
381 double
382 TextFont::Height () const
384 return extents.height;
387 static int
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)
395 return -1;
397 return cmp > 0 ? 1 : 0;
400 GlyphInfo *
401 TextFont::GetGlyphInfo (FontFace *face, gunichar unichar, guint32 index)
403 gint64 now = get_now ();
404 GlyphInfo glyph, *slot;
405 int i;
407 for (i = 0; i < n_glyphs; i++) {
408 if (glyphs[i].unichar == unichar) {
409 slot = &glyphs[i];
410 slot->atime = now;
411 return slot;
415 glyph.unichar = unichar;
416 glyph.index = index;
417 glyph.face = face;
418 glyph.atime = now;
419 glyph.path = NULL;
421 if (desc != NULL) {
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))
431 return NULL;
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 = %lld\n", i, glyphs[i].atime);
440 slot = &glyphs[n_glyphs - 1];
442 if (slot->path)
443 moon_path_destroy (slot->path);
444 } else {
445 slot = &glyphs[n_glyphs++];
448 memcpy (slot, &glyph, sizeof (GlyphInfo));
450 return slot;
453 //static GlyphInfo ZeroWidthNoBreakSpace = {
454 // 0xFEFF, 0, { 0.0, 0.0, 0.0, 0.0, 0.0 }, NULL, 0, 0
455 //};
457 GlyphInfo *
458 TextFont::GetGlyphInfo (gunichar unichar)
460 FontFace *face = NULL;
461 guint32 index;
462 int i;
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) {
470 face = faces[i];
471 break;
475 if (face == NULL) {
476 // draw the empty glyph from the primary face
477 face = faces[0];
478 index = 0;
481 return GetGlyphInfo (face, unichar, index);
484 GlyphInfo *
485 TextFont::GetGlyphInfoByIndex (guint32 index)
487 gunichar unichar;
489 unichar = faces[0]->GetCharFromIndex (index);
491 return GetGlyphInfo (faces[0], unichar, index);
494 double
495 TextFont::UnderlinePosition () const
497 return extents.underline_position;
500 double
501 TextFont::UnderlineThickness () const
503 return extents.underline_thickness;
506 void
507 TextFont::Path (cairo_t *cr, GlyphInfo *glyph, double x, double y)
509 if (!glyph->path || !(&glyph->path->cairo)->data)
510 return;
512 cairo_translate (cr, x, y);
513 cairo_append_path (cr, &glyph->path->cairo);
514 cairo_translate (cr, -x, -y);
517 void
518 TextFont::Path (cairo_t *cr, gunichar unichar, double x, double y)
520 GlyphInfo *glyph;
522 if (!(glyph = GetGlyphInfo (unichar)))
523 return;
525 Path (cr, glyph, x, y);
528 static void
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);
541 break;
542 case CAIRO_PATH_LINE_TO:
543 moon_line_to (mpath, data[1].point.x + x, data[1].point.y + y);
544 break;
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);
549 break;
550 case CAIRO_PATH_CLOSE_PATH:
551 break;
556 void
557 TextFont::AppendPath (moon_path *path, GlyphInfo *glyph, double x, double y)
559 if (!glyph->path || !(&glyph->path->cairo)->data)
560 return;
562 moon_append_path_with_origin (path, &glyph->path->cairo, x, y);
565 void
566 TextFont::AppendPath (moon_path *path, gunichar unichar, double x, double y)
568 GlyphInfo *glyph;
570 if (!(glyph = GetGlyphInfo (unichar)))
571 return;
573 AppendPath (path, glyph, x, y);
577 TextFontDescription::TextFontDescription ()
579 changed = true;
580 font = NULL;
582 language = NULL;
583 family = NULL;
584 source = NULL;
586 style = FontStylesNormal;
587 weight = FontWeightsNormal;
588 stretch = FontStretchesNormal;
589 size = 14.666666984558105;
592 TextFontDescription::~TextFontDescription ()
594 g_free (language);
595 g_free (source);
596 g_free (family);
597 delete font;
600 void
601 TextFontDescription::Reload ()
603 // load the new font first - trick to hit the cache for old FontFaces
604 TextFont *ttf = TextFont::Load (this);
605 changed = false;
606 delete font;
607 font = ttf;
610 TextFont *
611 TextFontDescription::GetFont ()
613 if (changed)
614 Reload ();
616 return font;
619 const char *
620 TextFontDescription::GetSource () const
622 return source;
625 bool
626 TextFontDescription::SetSource (const char *source)
628 bool changed;
630 if (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;
635 changed = true;
636 } else {
637 changed = false;
639 } else {
640 if (this->source) {
641 g_free (this->source);
642 this->source = NULL;
643 this->changed = true;
644 changed = true;
645 } else {
646 changed = false;
650 return changed;
653 char **
654 TextFontDescription::GetFamilies () const
656 char **families;
658 if (!family)
659 return NULL;
661 if ((families = g_strsplit (family, ",", -1))) {
662 for (int i = 0; families[i]; i++)
663 g_strstrip (families[i]);
666 return families;
669 const char *
670 TextFontDescription::GetFamily () const
672 return family;
675 bool
676 TextFontDescription::SetFamily (const char *family)
678 bool changed;
680 if (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;
685 changed = true;
686 } else {
687 changed = false;
689 } else {
690 if (this->family) {
691 g_free (this->family);
692 this->family = NULL;
693 this->changed = true;
694 changed = true;
695 } else {
696 changed = false;
700 return changed;
703 const char *
704 TextFontDescription::GetLanguage () const
706 return language;
709 bool
710 TextFontDescription::SetLanguage (const char *lang)
712 bool changed;
713 char *canon;
715 if (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;
722 changed = true;
723 } else {
724 g_free (canon);
725 changed = false;
727 } else {
728 if (this->language) {
729 g_free (this->language);
730 this->language = NULL;
731 this->changed = true;
732 changed = true;
733 } else {
734 changed = false;
738 return changed;
741 FontStyles
742 TextFontDescription::GetStyle () const
744 return style;
747 bool
748 TextFontDescription::SetStyle (FontStyles style)
750 bool changed = this->style != style;
752 if (changed) {
753 this->style = style;
754 this->changed = true;
757 return changed;
760 FontWeights
761 TextFontDescription::GetWeight () const
763 return weight;
766 bool
767 TextFontDescription::SetWeight (FontWeights weight)
769 bool changed = this->weight != weight;
771 if (changed) {
772 this->weight = weight;
773 this->changed = true;
776 return changed;
779 FontStretches
780 TextFontDescription::GetStretch () const
782 return stretch;
785 bool
786 TextFontDescription::SetStretch (FontStretches stretch)
788 bool changed = this->stretch != stretch;
790 if (changed) {
791 this->stretch = stretch;
792 this->changed = true;
795 return changed;
798 double
799 TextFontDescription::GetSize () const
801 return size;
804 bool
805 TextFontDescription::SetSize (double size)
807 bool changed = this->size != size;
809 if (font)
810 font->SetSize (size);
812 this->size = size;
814 return changed;