]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkfont-x11.c
a0fc46aebb94ef4d48425a4666d6c0e6ac3cb32a
[~andy/gtk] / gdk / x11 / gdkfont-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <X11/Xlib.h>
28 #include <X11/Xos.h>
29
30 #include <pango/pangox.h>
31
32 #include "gdkfont.h"
33 #include "gdkprivate-x11.h"
34
35 static GHashTable *font_name_hash = NULL;
36 static GHashTable *fontset_name_hash = NULL;
37
38 static void
39 gdk_font_hash_insert (GdkFontType type, GdkFont *font, const gchar *font_name)
40 {
41   GdkFontPrivateX *private = (GdkFontPrivateX *)font;
42   GHashTable **hashp = (type == GDK_FONT_FONT) ?
43     &font_name_hash : &fontset_name_hash;
44
45   if (!*hashp)
46     *hashp = g_hash_table_new (g_str_hash, g_str_equal);
47
48   private->names = g_slist_prepend (private->names, g_strdup (font_name));
49   g_hash_table_insert (*hashp, private->names->data, font);
50 }
51
52 static void
53 gdk_font_hash_remove (GdkFontType type, GdkFont *font)
54 {
55   GdkFontPrivateX *private = (GdkFontPrivateX *)font;
56   GSList *tmp_list;
57   GHashTable *hash = (type == GDK_FONT_FONT) ?
58     font_name_hash : fontset_name_hash;
59
60   tmp_list = private->names;
61   while (tmp_list)
62     {
63       g_hash_table_remove (hash, tmp_list->data);
64       g_free (tmp_list->data);
65       
66       tmp_list = tmp_list->next;
67     }
68
69   g_slist_free (private->names);
70   private->names = NULL;
71 }
72
73 static GdkFont *
74 gdk_font_hash_lookup (GdkFontType type, const gchar *font_name)
75 {
76   GdkFont *result;
77   GHashTable *hash = (type == GDK_FONT_FONT) ?
78     font_name_hash : fontset_name_hash;
79
80   if (!hash)
81     return NULL;
82   else
83     {
84       result = g_hash_table_lookup (hash, font_name);
85       if (result)
86         gdk_font_ref (result);
87       
88       return result;
89     }
90 }
91
92 GdkFont*
93 gdk_font_load (const gchar *font_name)
94 {
95   GdkFont *font;
96   GdkFontPrivateX *private;
97   XFontStruct *xfont;
98
99   g_return_val_if_fail (font_name != NULL, NULL);
100
101   font = gdk_font_hash_lookup (GDK_FONT_FONT, font_name);
102   if (font)
103     return font;
104
105   xfont = XLoadQueryFont (gdk_display, font_name);
106   if (xfont == NULL)
107     return NULL;
108
109   font = gdk_font_lookup (xfont->fid);
110   if (font != NULL)
111     {
112       private = (GdkFontPrivateX *) font;
113       if (xfont != private->xfont)
114         XFreeFont (gdk_display, xfont);
115
116       gdk_font_ref (font);
117     }
118   else
119     {
120       private = g_new (GdkFontPrivateX, 1);
121       private->xdisplay = gdk_display;
122       private->xfont = xfont;
123       private->base.ref_count = 1;
124       private->names = NULL;
125  
126       font = (GdkFont*) private;
127       font->type = GDK_FONT_FONT;
128       font->ascent =  xfont->ascent;
129       font->descent = xfont->descent;
130
131       gdk_xid_table_insert (&xfont->fid, font);
132     }
133
134   gdk_font_hash_insert (GDK_FONT_FONT, font, font_name);
135
136   return font;
137 }
138
139 static char *
140 gdk_font_charset_for_locale ()
141 {
142   static char *charset_map[][2] = {
143     { "ANSI_X3.4-1968", "iso8859-1" },
144     { "ISO-8859-1", "iso8859-1" },
145     { "ISO-8859-2", "iso8859-2" },
146     { "ISO-8859-3", "iso8859-3" },
147     { "ISO-8859-4", "iso8859-4" },
148     { "ISO-8859-5", "iso8859-5" },
149     { "ISO-8859-6", "iso8859-6" },
150     { "ISO-8859-7", "iso8859-7" },
151     { "ISO-8859-8", "iso8859-8" },
152     { "ISO-8859-9", "iso8859-9" },
153   };
154
155   char *codeset = g_get_codeset ();
156   char *result = NULL;
157   int i;
158   
159   for (i=0; i < G_N_ELEMENTS (charset_map); i++)
160     if (strcmp (charset_map[i][0], codeset) == 0)
161       {
162         result = charset_map[i][1];
163         break;
164       }
165
166   g_free (codeset);
167
168   if (result)
169     return g_strdup (result);
170   else
171     return g_strdup ("iso-8859-1");
172 };
173
174 /**
175  * gdk_font_from_description:
176  * @font_desc: a #PangoFontDescription.
177  * 
178  * Load a #GdkFont based on a Pango font description. This font will
179  * only be an approximation of the Pango font, and
180  * internationalization will not be handled correctly. This function
181  * should only be used for legacy code that cannot be easily converted
182  * to use Pango. Using Pango directly will produce better results.
183  * 
184  * Return value: the newly loaded font, or %NULL if the font
185  * cannot be loaded.
186  **/
187 GdkFont*
188 gdk_font_from_description (PangoFontDescription *font_desc)
189 {
190   PangoFontMap *font_map;
191   PangoFont *font;
192   GdkFont *result = NULL;
193
194   g_return_val_if_fail (font_desc != NULL, NULL);
195
196   font_map = pango_x_font_map_for_display (GDK_DISPLAY ());
197   font = pango_font_map_load_font (font_map, font_desc);
198
199   if (font)
200     {
201       gchar *charset = gdk_font_charset_for_locale ();
202       gint n_subfonts;
203       PangoXSubfont *subfont_ids;
204       gint *subfont_charsets;
205
206       n_subfonts = pango_x_list_subfonts (font, &charset, 1,
207                                           &subfont_ids, &subfont_charsets);
208       if (n_subfonts > 0)
209         {
210           gchar *xlfd = pango_x_font_subfont_xlfd (font, subfont_ids[0]);
211           result = gdk_font_load (xlfd);
212
213           g_free (xlfd);
214         }
215
216       g_free (subfont_ids);
217       g_free (subfont_charsets);
218
219       g_free (charset);
220       g_object_unref (G_OBJECT (font));
221     }
222
223   return result;
224 }
225
226 GdkFont*
227 gdk_fontset_load (const gchar *fontset_name)
228 {
229   GdkFont *font;
230   GdkFontPrivateX *private;
231   XFontSet fontset;
232   gint  missing_charset_count;
233   gchar **missing_charset_list;
234   gchar *def_string;
235
236   font = gdk_font_hash_lookup (GDK_FONT_FONTSET, fontset_name);
237   if (font)
238     return font;
239
240   private = g_new (GdkFontPrivateX, 1);
241   font = (GdkFont*) private;
242
243   private->xdisplay = gdk_display;
244   fontset = XCreateFontSet (gdk_display, fontset_name,
245                             &missing_charset_list, &missing_charset_count,
246                             &def_string);
247
248   if (missing_charset_count)
249     {
250       gint i;
251       g_warning ("Missing charsets in FontSet creation\n");
252       for (i=0;i<missing_charset_count;i++)
253         g_warning ("    %s\n", missing_charset_list[i]);
254       XFreeStringList (missing_charset_list);
255     }
256
257   private->base.ref_count = 1;
258
259   if (!fontset)
260     {
261       g_free (font);
262       return NULL;
263     }
264   else
265     {
266       gint num_fonts;
267       gint i;
268       XFontStruct **font_structs;
269       gchar **font_names;
270       
271       private->xfont = fontset;
272       font->type = GDK_FONT_FONTSET;
273       num_fonts = XFontsOfFontSet (fontset, &font_structs, &font_names);
274
275       font->ascent = font->descent = 0;
276       
277       for (i = 0; i < num_fonts; i++)
278         {
279           font->ascent = MAX (font->ascent, font_structs[i]->ascent);
280           font->descent = MAX (font->descent, font_structs[i]->descent);
281         }
282
283       private->names = NULL;
284       gdk_font_hash_insert (GDK_FONT_FONTSET, font, fontset_name);
285       
286       return font;
287     }
288 }
289
290 void
291 _gdk_font_destroy (GdkFont *font)
292 {
293   GdkFontPrivateX *private = (GdkFontPrivateX *)font;
294   
295   gdk_font_hash_remove (font->type, font);
296       
297   switch (font->type)
298     {
299     case GDK_FONT_FONT:
300       gdk_xid_table_remove (((XFontStruct *) private->xfont)->fid);
301       XFreeFont (private->xdisplay, (XFontStruct *) private->xfont);
302       break;
303     case GDK_FONT_FONTSET:
304       XFreeFontSet (private->xdisplay, (XFontSet) private->xfont);
305       break;
306     default:
307       g_error ("unknown font type.");
308       break;
309     }
310   g_free (font);
311 }
312
313 gint
314 _gdk_font_strlen (GdkFont     *font,
315                   const gchar *str)
316 {
317   GdkFontPrivateX *font_private;
318   gint length = 0;
319
320   g_return_val_if_fail (font != NULL, -1);
321   g_return_val_if_fail (str != NULL, -1);
322
323   font_private = (GdkFontPrivateX*) font;
324
325   if (font->type == GDK_FONT_FONT)
326     {
327       XFontStruct *xfont = (XFontStruct *) font_private->xfont;
328       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
329         {
330           length = strlen (str);
331         }
332       else
333         {
334           guint16 *string_2b = (guint16 *)str;
335             
336           while (*(string_2b++))
337             length++;
338         }
339     }
340   else if (font->type == GDK_FONT_FONTSET)
341     {
342       length = strlen (str);
343     }
344   else
345     g_error("undefined font type\n");
346
347   return length;
348 }
349
350 gint
351 gdk_font_id (const GdkFont *font)
352 {
353   const GdkFontPrivateX *font_private;
354
355   g_return_val_if_fail (font != NULL, 0);
356
357   font_private = (const GdkFontPrivateX*) font;
358
359   if (font->type == GDK_FONT_FONT)
360     {
361       return ((XFontStruct *) font_private->xfont)->fid;
362     }
363   else
364     {
365       return 0;
366     }
367 }
368
369 gboolean
370 gdk_font_equal (const GdkFont *fonta,
371                 const GdkFont *fontb)
372 {
373   const GdkFontPrivateX *privatea;
374   const GdkFontPrivateX *privateb;
375
376   g_return_val_if_fail (fonta != NULL, FALSE);
377   g_return_val_if_fail (fontb != NULL, FALSE);
378
379   privatea = (const GdkFontPrivateX*) fonta;
380   privateb = (const GdkFontPrivateX*) fontb;
381
382   if (fonta->type == GDK_FONT_FONT && fontb->type == GDK_FONT_FONT)
383     {
384       return (((XFontStruct *) privatea->xfont)->fid ==
385               ((XFontStruct *) privateb->xfont)->fid);
386     }
387   else if (fonta->type == GDK_FONT_FONTSET && fontb->type == GDK_FONT_FONTSET)
388     {
389       gchar *namea, *nameb;
390
391       namea = XBaseFontNameListOfFontSet((XFontSet) privatea->xfont);
392       nameb = XBaseFontNameListOfFontSet((XFontSet) privateb->xfont);
393       
394       return (strcmp(namea, nameb) == 0);
395     }
396   else
397     /* fontset != font */
398     return 0;
399 }
400
401 gint
402 gdk_text_width (GdkFont      *font,
403                 const gchar  *text,
404                 gint          text_length)
405 {
406   GdkFontPrivateX *private;
407   gint width;
408   XFontStruct *xfont;
409   XFontSet fontset;
410
411   g_return_val_if_fail (font != NULL, -1);
412   g_return_val_if_fail (text != NULL, -1);
413
414   private = (GdkFontPrivateX*) font;
415
416   switch (font->type)
417     {
418     case GDK_FONT_FONT:
419       xfont = (XFontStruct *) private->xfont;
420       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
421         {
422           width = XTextWidth (xfont, text, text_length);
423         }
424       else
425         {
426           width = XTextWidth16 (xfont, (XChar2b *) text, text_length / 2);
427         }
428       break;
429     case GDK_FONT_FONTSET:
430       fontset = (XFontSet) private->xfont;
431       width = XmbTextEscapement (fontset, text, text_length);
432       break;
433     default:
434       width = 0;
435     }
436   return width;
437 }
438
439 gint
440 gdk_text_width_wc (GdkFont        *font,
441                    const GdkWChar *text,
442                    gint            text_length)
443 {
444   GdkFontPrivateX *private;
445   gint width;
446   XFontStruct *xfont;
447   XFontSet fontset;
448
449   g_return_val_if_fail (font != NULL, -1);
450   g_return_val_if_fail (text != NULL, -1);
451
452   private = (GdkFontPrivateX*) font;
453
454   switch (font->type)
455     {
456     case GDK_FONT_FONT:
457       xfont = (XFontStruct *) private->xfont;
458       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
459         {
460           gchar *text_8bit;
461           gint i;
462           text_8bit = g_new (gchar, text_length);
463           for (i=0; i<text_length; i++) text_8bit[i] = text[i];
464           width = XTextWidth (xfont, text_8bit, text_length);
465           g_free (text_8bit);
466         }
467       else
468         {
469           width = 0;
470         }
471       break;
472     case GDK_FONT_FONTSET:
473       if (sizeof(GdkWChar) == sizeof(wchar_t))
474         {
475           fontset = (XFontSet) private->xfont;
476           width = XwcTextEscapement (fontset, (wchar_t *)text, text_length);
477         }
478       else
479         {
480           wchar_t *text_wchar;
481           gint i;
482           fontset = (XFontSet) private->xfont;
483           text_wchar = g_new(wchar_t, text_length);
484           for (i=0; i<text_length; i++) text_wchar[i] = text[i];
485           width = XwcTextEscapement (fontset, text_wchar, text_length);
486           g_free (text_wchar);
487         }
488       break;
489     default:
490       width = 0;
491     }
492   return width;
493 }
494
495 void
496 gdk_text_extents (GdkFont     *font,
497                   const gchar *text,
498                   gint         text_length,
499                   gint        *lbearing,
500                   gint        *rbearing,
501                   gint        *width,
502                   gint        *ascent,
503                   gint        *descent)
504 {
505   GdkFontPrivateX *private;
506   XCharStruct overall;
507   XFontStruct *xfont;
508   XFontSet    fontset;
509   XRectangle  ink, logical;
510   int direction;
511   int font_ascent;
512   int font_descent;
513
514   g_return_if_fail (font != NULL);
515   g_return_if_fail (text != NULL);
516
517   private = (GdkFontPrivateX*) font;
518
519   switch (font->type)
520     {
521     case GDK_FONT_FONT:
522       xfont = (XFontStruct *) private->xfont;
523       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
524         {
525           XTextExtents (xfont, text, text_length,
526                         &direction, &font_ascent, &font_descent,
527                         &overall);
528         }
529       else
530         {
531           XTextExtents16 (xfont, (XChar2b *) text, text_length / 2,
532                           &direction, &font_ascent, &font_descent,
533                           &overall);
534         }
535       if (lbearing)
536         *lbearing = overall.lbearing;
537       if (rbearing)
538         *rbearing = overall.rbearing;
539       if (width)
540         *width = overall.width;
541       if (ascent)
542         *ascent = overall.ascent;
543       if (descent)
544         *descent = overall.descent;
545       break;
546     case GDK_FONT_FONTSET:
547       fontset = (XFontSet) private->xfont;
548       XmbTextExtents (fontset, text, text_length, &ink, &logical);
549       if (lbearing)
550         *lbearing = ink.x;
551       if (rbearing)
552         *rbearing = ink.x + ink.width;
553       if (width)
554         *width = logical.width;
555       if (ascent)
556         *ascent = -ink.y;
557       if (descent)
558         *descent = ink.y + ink.height;
559       break;
560     }
561
562 }
563
564 void
565 gdk_text_extents_wc (GdkFont        *font,
566                      const GdkWChar *text,
567                      gint            text_length,
568                      gint           *lbearing,
569                      gint           *rbearing,
570                      gint           *width,
571                      gint           *ascent,
572                      gint           *descent)
573 {
574   GdkFontPrivateX *private;
575   XCharStruct overall;
576   XFontStruct *xfont;
577   XFontSet    fontset;
578   XRectangle  ink, logical;
579   int direction;
580   int font_ascent;
581   int font_descent;
582
583   g_return_if_fail (font != NULL);
584   g_return_if_fail (text != NULL);
585
586   private = (GdkFontPrivateX*) font;
587
588   switch (font->type)
589     {
590     case GDK_FONT_FONT:
591       {
592         gchar *text_8bit;
593         gint i;
594
595         xfont = (XFontStruct *) private->xfont;
596         g_return_if_fail ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0));
597
598         text_8bit = g_new (gchar, text_length);
599         for (i=0; i<text_length; i++) 
600           text_8bit[i] = text[i];
601
602         XTextExtents (xfont, text_8bit, text_length,
603                       &direction, &font_ascent, &font_descent,
604                       &overall);
605         g_free (text_8bit);
606         
607         if (lbearing)
608           *lbearing = overall.lbearing;
609         if (rbearing)
610           *rbearing = overall.rbearing;
611         if (width)
612           *width = overall.width;
613         if (ascent)
614           *ascent = overall.ascent;
615         if (descent)
616           *descent = overall.descent;
617         break;
618       }
619     case GDK_FONT_FONTSET:
620       fontset = (XFontSet) private->xfont;
621
622       if (sizeof(GdkWChar) == sizeof(wchar_t))
623         XwcTextExtents (fontset, (wchar_t *)text, text_length, &ink, &logical);
624       else
625         {
626           wchar_t *text_wchar;
627           gint i;
628           
629           text_wchar = g_new (wchar_t, text_length);
630           for (i = 0; i < text_length; i++)
631             text_wchar[i] = text[i];
632           XwcTextExtents (fontset, text_wchar, text_length, &ink, &logical);
633           g_free (text_wchar);
634         }
635       if (lbearing)
636         *lbearing = ink.x;
637       if (rbearing)
638         *rbearing = ink.x + ink.width;
639       if (width)
640         *width = logical.width;
641       if (ascent)
642         *ascent = -ink.y;
643       if (descent)
644         *descent = ink.y + ink.height;
645       break;
646     }
647
648 }