Added speed-up for font-loading (a bit simplistic),
[wxWidgets.git] / src / unix / fontutil.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: unix/fontutil.cpp
3 // Purpose: Font helper functions for X11 (GDK/X)
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 05.11.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "fontutil.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #endif // PCH
33
34 #ifdef __X__
35 #ifdef __VMS__
36 #pragma message disable nosimpint
37 #endif
38 #include <X11/Xlib.h>
39 #ifdef __VMS__
40 #pragma message enable nosimpint
41 #endif
42
43 #include "wx/utils.h" // for wxGetDisplay()
44 #elif defined(__WXGTK__)
45 #include "gdk/gdk.h"
46 #endif
47
48 #include "wx/fontutil.h"
49 #include "wx/fontmap.h"
50 #include "wx/tokenzr.h"
51 #include "wx/hash.h"
52 #include "wx/module.h"
53
54 // ----------------------------------------------------------------------------
55 // private data
56 // ----------------------------------------------------------------------------
57
58 static wxHashTable *g_fontHash = (wxHashTable*) NULL;
59
60 // ----------------------------------------------------------------------------
61 // private functions
62 // ----------------------------------------------------------------------------
63
64 // define the functions to create and destroy native fonts for this toolkit
65 #ifdef __X__
66 static inline wxNativeFont wxLoadFont(const wxString& fontSpec)
67 {
68 return XLoadQueryFont((Display *)wxGetDisplay(), fontSpec);
69 }
70
71 static inline void wxFreeFont(wxNativeFont font)
72 {
73 XFreeFont((Display *)wxGetDisplay(), (XFontStruct *)font);
74 }
75 #elif defined(__WXGTK__)
76 static inline wxNativeFont wxLoadFont(const wxString& fontSpec)
77 {
78 return gdk_font_load( wxConvertWX2MB(fontSpec) );
79 }
80
81 static inline void wxFreeFont(wxNativeFont font)
82 {
83 gdk_font_unref(font);
84 }
85 #else
86 #error "Unknown GUI toolkit"
87 #endif
88
89 static bool wxTestFontSpec(const wxString& fontspec);
90
91 static wxNativeFont wxLoadQueryFont(int pointSize,
92 int family,
93 int style,
94 int weight,
95 bool underlined,
96 const wxString& facename,
97 const wxString& xregistry,
98 const wxString& xencoding);
99
100 // ============================================================================
101 // implementation
102 // ============================================================================
103
104 // ----------------------------------------------------------------------------
105 // wxNativeEncodingInfo
106 // ----------------------------------------------------------------------------
107
108 // convert to/from the string representation: format is
109 // registry-encoding[-facename]
110 bool wxNativeEncodingInfo::FromString(const wxString& s)
111 {
112 wxStringTokenizer tokenizer(s, _T("-"));
113
114 xregistry = tokenizer.GetNextToken();
115 if ( !xregistry )
116 return FALSE;
117
118 xencoding = tokenizer.GetNextToken();
119 if ( !xencoding )
120 return FALSE;
121
122 // ok even if empty
123 facename = tokenizer.GetNextToken();
124
125 return TRUE;
126 }
127
128 wxString wxNativeEncodingInfo::ToString() const
129 {
130 wxString s;
131 s << xregistry << _T('-') << xencoding;
132 if ( !!facename )
133 {
134 s << _T('-') << facename;
135 }
136
137 return s;
138 }
139
140 // ----------------------------------------------------------------------------
141 // common functions
142 // ----------------------------------------------------------------------------
143
144 bool wxGetNativeFontEncoding(wxFontEncoding encoding,
145 wxNativeEncodingInfo *info)
146 {
147 wxCHECK_MSG( info, FALSE, _T("bad pointer in wxGetNativeFontEncoding") );
148
149 if ( encoding == wxFONTENCODING_DEFAULT )
150 {
151 encoding = wxFont::GetDefaultEncoding();
152 }
153
154 switch ( encoding )
155 {
156 case wxFONTENCODING_ISO8859_1:
157 case wxFONTENCODING_ISO8859_2:
158 case wxFONTENCODING_ISO8859_3:
159 case wxFONTENCODING_ISO8859_4:
160 case wxFONTENCODING_ISO8859_5:
161 case wxFONTENCODING_ISO8859_6:
162 case wxFONTENCODING_ISO8859_7:
163 case wxFONTENCODING_ISO8859_8:
164 case wxFONTENCODING_ISO8859_9:
165 case wxFONTENCODING_ISO8859_10:
166 case wxFONTENCODING_ISO8859_11:
167 case wxFONTENCODING_ISO8859_13:
168 case wxFONTENCODING_ISO8859_14:
169 case wxFONTENCODING_ISO8859_15:
170 {
171 int cp = encoding - wxFONTENCODING_ISO8859_1 + 1;
172 info->xregistry = wxT("iso8859");
173 info->xencoding.Printf(wxT("%d"), cp);
174 }
175 break;
176
177 case wxFONTENCODING_KOI8:
178 info->xregistry = wxT("koi8");
179
180 // we don't make distinction between koi8-r and koi8-u (so far)
181 info->xencoding = wxT("*");
182 break;
183
184 case wxFONTENCODING_CP1250:
185 case wxFONTENCODING_CP1251:
186 case wxFONTENCODING_CP1252:
187 case wxFONTENCODING_CP1253:
188 case wxFONTENCODING_CP1254:
189 case wxFONTENCODING_CP1255:
190 case wxFONTENCODING_CP1256:
191 case wxFONTENCODING_CP1257:
192 {
193 int cp = encoding - wxFONTENCODING_CP1250 + 1250;
194 info->xregistry = wxT("microsoft");
195 info->xencoding.Printf(wxT("cp%d"), cp);
196 }
197 break;
198
199 case wxFONTENCODING_SYSTEM:
200 info->xregistry =
201 info->xencoding = wxT("*");
202 break;
203
204 default:
205 // don't know how to translate this encoding into X fontspec
206 return FALSE;
207 }
208
209 return TRUE;
210 }
211
212 bool wxTestFontEncoding(const wxNativeEncodingInfo& info)
213 {
214 wxString fontspec;
215 fontspec.Printf(_T("-*-%s-*-*-*-*-*-*-*-*-*-*-%s-%s"),
216 !info.facename ? _T("*") : info.facename.c_str(),
217 info.xregistry.c_str(),
218 info.xencoding.c_str());
219
220 return wxTestFontSpec(fontspec);
221 }
222
223 // ----------------------------------------------------------------------------
224 // X-specific functions
225 // ----------------------------------------------------------------------------
226
227 wxNativeFont wxLoadQueryNearestFont(int pointSize,
228 int family,
229 int style,
230 int weight,
231 bool underlined,
232 const wxString &facename,
233 wxFontEncoding encoding)
234 {
235 if ( encoding == wxFONTENCODING_DEFAULT )
236 {
237 encoding = wxFont::GetDefaultEncoding();
238 }
239
240 // first determine the encoding - if the font doesn't exist at all in this
241 // encoding, it's useless to do all other approximations (i.e. size,
242 // family &c don't matter much)
243 wxNativeEncodingInfo info;
244 if ( encoding == wxFONTENCODING_SYSTEM )
245 {
246 // This will always work so we don't test to save time
247 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
248 }
249 else
250 {
251 if ( !wxGetNativeFontEncoding(encoding, &info) ||
252 !wxTestFontEncoding(info) )
253 {
254 if ( !wxTheFontMapper->GetAltForEncoding(encoding, &info) )
255 {
256 // unspported encoding - replace it with the default
257 //
258 // NB: we can't just return 0 from here because wxGTK code doesn't
259 // check for it (i.e. it supposes that we'll always succeed),
260 // so it would provoke a crash
261 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
262 }
263 }
264 }
265
266 // OK, we have the correct xregistry/xencoding in info structure
267 wxNativeFont font = wxLoadQueryFont( pointSize, family, style, weight,
268 underlined, facename,
269 info.xregistry, info.xencoding );
270
271 if ( !font )
272 {
273 // search up and down by stepsize 10
274 int max_size = pointSize + 20 * (1 + (pointSize/180));
275 int min_size = pointSize - 20 * (1 + (pointSize/180));
276
277 int i;
278
279 // Search for smaller size (approx.)
280 for ( i = pointSize - 10; !font && i >= 10 && i >= min_size; i -= 10 )
281 {
282 font = wxLoadQueryFont(i, family, style, weight, underlined,
283 facename, info.xregistry, info.xencoding);
284 }
285
286 // Search for larger size (approx.)
287 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
288 {
289 font = wxLoadQueryFont(i, family, style, weight, underlined,
290 facename, info.xregistry, info.xencoding);
291 }
292
293 // Try default family
294 if ( !font && family != wxDEFAULT )
295 {
296 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
297 underlined, facename,
298 info.xregistry, info.xencoding );
299 }
300
301 // Bogus font I
302 if ( !font )
303 {
304 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
305 underlined, facename,
306 info.xregistry, info.xencoding);
307 }
308
309 // Bogus font II
310 if ( !font )
311 {
312 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
313 underlined, wxEmptyString,
314 info.xregistry, info.xencoding);
315 }
316 }
317
318 return font;
319 }
320
321 // ----------------------------------------------------------------------------
322 // private functions
323 // ----------------------------------------------------------------------------
324
325 // returns TRUE if there are any fonts matching this font spec
326 static bool wxTestFontSpec(const wxString& fontspec)
327 {
328 // some X servers will fail to load this font because there are too many
329 // matches so we must test explicitly for this
330 if ( fontspec == _T("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
331 {
332 return TRUE;
333 }
334
335 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
336 if (test)
337 {
338 // printf( "speed up\n" );
339 return TRUE;
340 }
341
342 test = wxLoadFont(fontspec);
343 g_fontHash->Put( fontspec, (wxObject*) test );
344
345 if ( test )
346 {
347 wxFreeFont(test);
348
349 return TRUE;
350 }
351 else
352 {
353 return FALSE;
354 }
355 }
356
357 static wxNativeFont wxLoadQueryFont(int pointSize,
358 int family,
359 int style,
360 int weight,
361 bool WXUNUSED(underlined),
362 const wxString& facename,
363 const wxString& xregistry,
364 const wxString& xencoding)
365 {
366 wxString xfamily;
367 switch (family)
368 {
369 case wxDECORATIVE: xfamily = wxT("lucida"); break;
370 case wxROMAN: xfamily = wxT("times"); break;
371 case wxMODERN: xfamily = wxT("courier"); break;
372 case wxSWISS: xfamily = wxT("helvetica"); break;
373 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
374 case wxSCRIPT: xfamily = wxT("utopia"); break;
375 default: xfamily = wxT("*");
376 }
377
378 wxString fontSpec;
379 if (!facename.IsEmpty())
380 {
381 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
382 facename.c_str());
383
384 if ( wxTestFontSpec(fontSpec) )
385 {
386 xfamily = facename;
387 }
388 //else: no such family, use default one instead
389 }
390
391 wxString xstyle;
392 switch (style)
393 {
394 case wxITALIC: xstyle = wxT("i"); break;
395 case wxSLANT: xstyle = wxT("o"); break;
396 case wxNORMAL: xstyle = wxT("r"); break;
397 default: xstyle = wxT("*"); break;
398 }
399
400 wxString xweight;
401 switch (weight)
402 {
403 case wxBOLD:
404 {
405 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
406 xfamily.c_str());
407 if ( wxTestFontSpec(fontSpec) )
408 {
409 xweight = wxT("bold");
410 break;
411 }
412 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
413 xfamily.c_str());
414 if ( wxTestFontSpec(fontSpec) )
415 {
416 xweight = wxT("heavy");
417 break;
418 }
419 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
420 xfamily.c_str());
421 if ( wxTestFontSpec(fontSpec) )
422 {
423 xweight = wxT("extrabold");
424 break;
425 }
426 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
427 xfamily.c_str());
428 if ( wxTestFontSpec(fontSpec) )
429 {
430 xweight = wxT("demibold");
431 break;
432 }
433 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
434 xfamily.c_str());
435 if ( wxTestFontSpec(fontSpec) )
436 {
437 xweight = wxT("black");
438 break;
439 }
440 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
441 xfamily.c_str());
442 if ( wxTestFontSpec(fontSpec) )
443 {
444 xweight = wxT("ultrablack");
445 break;
446 }
447 }
448 break;
449 case wxLIGHT:
450 {
451 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
452 xfamily.c_str());
453 if ( wxTestFontSpec(fontSpec) )
454 {
455 xweight = wxT("light");
456 break;
457 }
458 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
459 xfamily.c_str());
460 if ( wxTestFontSpec(fontSpec) )
461 {
462 xweight = wxT("thin");
463 break;
464 }
465 }
466 break;
467 case wxNORMAL:
468 {
469 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
470 xfamily.c_str());
471 if ( wxTestFontSpec(fontSpec) )
472 {
473 xweight = wxT("medium");
474 break;
475 }
476 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
477 xfamily.c_str());
478 if ( wxTestFontSpec(fontSpec) )
479 {
480 xweight = wxT("normal");
481 break;
482 }
483 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
484 xfamily.c_str());
485 if ( wxTestFontSpec(fontSpec) )
486 {
487 xweight = wxT("regular");
488 break;
489 }
490 xweight = wxT("*");
491 }
492 break;
493 default: xweight = wxT("*"); break;
494 }
495
496 // construct the X font spec from our data
497 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%d-*-*-*-*-%s-%s"),
498 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
499 pointSize, xregistry.c_str(), xencoding.c_str());
500
501 return wxLoadFont(fontSpec);
502 }
503
504 // ----------------------------------------------------------------------------
505 // wxFontModule
506 // ----------------------------------------------------------------------------
507
508 class wxFontModule : public wxModule
509 {
510 public:
511 bool OnInit();
512 void OnExit();
513
514 private:
515 DECLARE_DYNAMIC_CLASS(wxFontModule)
516 };
517
518 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
519
520 bool wxFontModule::OnInit()
521 {
522 g_fontHash = new wxHashTable( wxKEY_STRING );
523
524 return TRUE;
525 }
526
527 void wxFontModule::OnExit()
528 {
529 delete g_fontHash;
530
531 g_fontHash = (wxHashTable *)NULL;
532 }