applied wxNativeFontInfo patch from Derry Bryson (with minor changes)
[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 wxString* xFontName);
100
101 // ============================================================================
102 // implementation
103 // ============================================================================
104
105 // ----------------------------------------------------------------------------
106 // wxNativeEncodingInfo
107 // ----------------------------------------------------------------------------
108
109 // convert to/from the string representation: format is
110 // encodingid;registry;encoding[;facename]
111 bool wxNativeEncodingInfo::FromString(const wxString& s)
112 {
113 wxStringTokenizer tokenizer(s, _T(";"));
114 // cannot use "-" because it may be part of encoding name
115
116 wxString encid = tokenizer.GetNextToken();
117 long enc;
118 if ( !encid.ToLong(&enc) )
119 return FALSE;
120 encoding = (wxFontEncoding)enc;
121
122 xregistry = tokenizer.GetNextToken();
123 if ( !xregistry )
124 return FALSE;
125
126 xencoding = tokenizer.GetNextToken();
127 if ( !xencoding )
128 return FALSE;
129
130 // ok even if empty
131 facename = tokenizer.GetNextToken();
132
133 return TRUE;
134 }
135
136 wxString wxNativeEncodingInfo::ToString() const
137 {
138 wxString s;
139 s << (long)encoding << _T(';') << xregistry << _T(';') << xencoding;
140 if ( !!facename )
141 {
142 s << _T(';') << facename;
143 }
144
145 return s;
146 }
147
148 // ----------------------------------------------------------------------------
149 // common functions
150 // ----------------------------------------------------------------------------
151
152 bool wxGetNativeFontEncoding(wxFontEncoding encoding,
153 wxNativeEncodingInfo *info)
154 {
155 wxCHECK_MSG( info, FALSE, _T("bad pointer in wxGetNativeFontEncoding") );
156
157 if ( encoding == wxFONTENCODING_DEFAULT )
158 {
159 encoding = wxFont::GetDefaultEncoding();
160 }
161
162 switch ( encoding )
163 {
164 case wxFONTENCODING_ISO8859_1:
165 case wxFONTENCODING_ISO8859_2:
166 case wxFONTENCODING_ISO8859_3:
167 case wxFONTENCODING_ISO8859_4:
168 case wxFONTENCODING_ISO8859_5:
169 case wxFONTENCODING_ISO8859_6:
170 case wxFONTENCODING_ISO8859_7:
171 case wxFONTENCODING_ISO8859_8:
172 case wxFONTENCODING_ISO8859_9:
173 case wxFONTENCODING_ISO8859_10:
174 case wxFONTENCODING_ISO8859_11:
175 case wxFONTENCODING_ISO8859_12:
176 case wxFONTENCODING_ISO8859_13:
177 case wxFONTENCODING_ISO8859_14:
178 case wxFONTENCODING_ISO8859_15:
179 {
180 int cp = encoding - wxFONTENCODING_ISO8859_1 + 1;
181 info->xregistry = wxT("iso8859");
182 info->xencoding.Printf(wxT("%d"), cp);
183 }
184 break;
185
186 case wxFONTENCODING_KOI8:
187 info->xregistry = wxT("koi8");
188
189 // we don't make distinction between koi8-r and koi8-u (so far)
190 info->xencoding = wxT("*");
191 break;
192
193 case wxFONTENCODING_CP1250:
194 case wxFONTENCODING_CP1251:
195 case wxFONTENCODING_CP1252:
196 case wxFONTENCODING_CP1253:
197 case wxFONTENCODING_CP1254:
198 case wxFONTENCODING_CP1255:
199 case wxFONTENCODING_CP1256:
200 case wxFONTENCODING_CP1257:
201 {
202 int cp = encoding - wxFONTENCODING_CP1250 + 1250;
203 info->xregistry = wxT("microsoft");
204 info->xencoding.Printf(wxT("cp%d"), cp);
205 }
206 break;
207
208 case wxFONTENCODING_SYSTEM:
209 info->xregistry =
210 info->xencoding = wxT("*");
211 break;
212
213 default:
214 // don't know how to translate this encoding into X fontspec
215 return FALSE;
216 }
217
218 info->encoding = encoding;
219
220 return TRUE;
221 }
222
223 bool wxTestFontEncoding(const wxNativeEncodingInfo& info)
224 {
225 wxString fontspec;
226 fontspec.Printf(_T("-*-%s-*-*-*-*-*-*-*-*-*-*-%s-%s"),
227 !info.facename ? _T("*") : info.facename.c_str(),
228 info.xregistry.c_str(),
229 info.xencoding.c_str());
230
231 return wxTestFontSpec(fontspec);
232 }
233
234 // ----------------------------------------------------------------------------
235 // X-specific functions
236 // ----------------------------------------------------------------------------
237
238 wxNativeFont wxLoadQueryNearestFont(int pointSize,
239 int family,
240 int style,
241 int weight,
242 bool underlined,
243 const wxString &facename,
244 wxFontEncoding encoding,
245 wxString* xFontName)
246 {
247 if ( encoding == wxFONTENCODING_DEFAULT )
248 {
249 encoding = wxFont::GetDefaultEncoding();
250 }
251
252 // first determine the encoding - if the font doesn't exist at all in this
253 // encoding, it's useless to do all other approximations (i.e. size,
254 // family &c don't matter much)
255 wxNativeEncodingInfo info;
256 if ( encoding == wxFONTENCODING_SYSTEM )
257 {
258 // This will always work so we don't test to save time
259 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
260 }
261 else
262 {
263 if ( !wxGetNativeFontEncoding(encoding, &info) ||
264 !wxTestFontEncoding(info) )
265 {
266 if ( !wxTheFontMapper->GetAltForEncoding(encoding, &info) )
267 {
268 // unspported encoding - replace it with the default
269 //
270 // NB: we can't just return 0 from here because wxGTK code doesn't
271 // check for it (i.e. it supposes that we'll always succeed),
272 // so it would provoke a crash
273 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
274 }
275 }
276 }
277
278 // OK, we have the correct xregistry/xencoding in info structure
279 wxNativeFont font = 0;
280
281 // if we already have the X font name, try to use it
282 if( xFontName && !xFontName->IsEmpty() )
283 font = wxLoadFont(*xFontName);
284
285 if( !font )
286 font = wxLoadQueryFont( pointSize, family, style, weight,
287 underlined, facename,
288 info.xregistry, info.xencoding,
289 xFontName );
290
291 if ( !font )
292 {
293 // search up and down by stepsize 10
294 int max_size = pointSize + 20 * (1 + (pointSize/180));
295 int min_size = pointSize - 20 * (1 + (pointSize/180));
296
297 int i;
298
299 // Search for smaller size (approx.)
300 for ( i = pointSize - 10; !font && i >= 10 && i >= min_size; i -= 10 )
301 {
302 font = wxLoadQueryFont(i, family, style, weight, underlined,
303 facename, info.xregistry, info.xencoding,
304 xFontName);
305 }
306
307 // Search for larger size (approx.)
308 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
309 {
310 font = wxLoadQueryFont(i, family, style, weight, underlined,
311 facename, info.xregistry, info.xencoding,
312 xFontName);
313 }
314
315 // Try default family
316 if ( !font && family != wxDEFAULT )
317 {
318 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
319 underlined, facename,
320 info.xregistry, info.xencoding,
321 xFontName );
322 }
323
324 // Bogus font I
325 if ( !font )
326 {
327 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
328 underlined, facename,
329 info.xregistry, info.xencoding,
330 xFontName);
331 }
332
333 // Bogus font II
334 if ( !font )
335 {
336 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
337 underlined, wxEmptyString,
338 info.xregistry, info.xencoding,
339 xFontName);
340 }
341 }
342
343 return font;
344 }
345
346 // ----------------------------------------------------------------------------
347 // private functions
348 // ----------------------------------------------------------------------------
349
350 // returns TRUE if there are any fonts matching this font spec
351 static bool wxTestFontSpec(const wxString& fontspec)
352 {
353 // some X servers will fail to load this font because there are too many
354 // matches so we must test explicitly for this
355 if ( fontspec == _T("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
356 {
357 return TRUE;
358 }
359
360 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
361 if (test)
362 {
363 // printf( "speed up\n" );
364 return TRUE;
365 }
366
367 test = wxLoadFont(fontspec);
368 g_fontHash->Put( fontspec, (wxObject*) test );
369
370 if ( test )
371 {
372 wxFreeFont(test);
373
374 return TRUE;
375 }
376 else
377 {
378 return FALSE;
379 }
380 }
381
382 static wxNativeFont wxLoadQueryFont(int pointSize,
383 int family,
384 int style,
385 int weight,
386 bool WXUNUSED(underlined),
387 const wxString& facename,
388 const wxString& xregistry,
389 const wxString& xencoding,
390 wxString* xFontName)
391 {
392 wxString xfamily;
393 switch (family)
394 {
395 case wxDECORATIVE: xfamily = wxT("lucida"); break;
396 case wxROMAN: xfamily = wxT("times"); break;
397 case wxMODERN: xfamily = wxT("courier"); break;
398 case wxSWISS: xfamily = wxT("helvetica"); break;
399 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
400 case wxSCRIPT: xfamily = wxT("utopia"); break;
401 default: xfamily = wxT("*");
402 }
403
404 wxString fontSpec;
405 if (!facename.IsEmpty())
406 {
407 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
408 facename.c_str());
409
410 if ( wxTestFontSpec(fontSpec) )
411 {
412 xfamily = facename;
413 }
414 //else: no such family, use default one instead
415 }
416
417 wxString xstyle;
418 switch (style)
419 {
420 case wxSLANT:
421 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
422 xfamily.c_str());
423 if ( wxTestFontSpec(fontSpec) )
424 {
425 xstyle = wxT("o");
426 break;
427 }
428 // fall through - try wxITALIC now
429
430 case wxITALIC:
431 fontSpec.Printf(wxT("-*-%s-*-i-*-*-*-*-*-*-*-*-*-*"),
432 xfamily.c_str());
433 if ( wxTestFontSpec(fontSpec) )
434 {
435 xstyle = wxT("i");
436 }
437 else if ( style == wxITALIC ) // and not wxSLANT
438 {
439 // try wxSLANT
440 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
441 xfamily.c_str());
442 if ( wxTestFontSpec(fontSpec) )
443 {
444 xstyle = wxT("o");
445 }
446 else
447 {
448 // no italic, no slant - leave default
449 xstyle = wxT("*");
450 }
451 }
452 break;
453
454 default:
455 wxFAIL_MSG(_T("unknown font style"));
456 // fall back to normal
457
458 case wxNORMAL:
459 xstyle = wxT("r");
460 break;
461 }
462
463 wxString xweight;
464 switch (weight)
465 {
466 case wxBOLD:
467 {
468 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
469 xfamily.c_str());
470 if ( wxTestFontSpec(fontSpec) )
471 {
472 xweight = wxT("bold");
473 break;
474 }
475 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
476 xfamily.c_str());
477 if ( wxTestFontSpec(fontSpec) )
478 {
479 xweight = wxT("heavy");
480 break;
481 }
482 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
483 xfamily.c_str());
484 if ( wxTestFontSpec(fontSpec) )
485 {
486 xweight = wxT("extrabold");
487 break;
488 }
489 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
490 xfamily.c_str());
491 if ( wxTestFontSpec(fontSpec) )
492 {
493 xweight = wxT("demibold");
494 break;
495 }
496 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
497 xfamily.c_str());
498 if ( wxTestFontSpec(fontSpec) )
499 {
500 xweight = wxT("black");
501 break;
502 }
503 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
504 xfamily.c_str());
505 if ( wxTestFontSpec(fontSpec) )
506 {
507 xweight = wxT("ultrablack");
508 break;
509 }
510 }
511 break;
512 case wxLIGHT:
513 {
514 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
515 xfamily.c_str());
516 if ( wxTestFontSpec(fontSpec) )
517 {
518 xweight = wxT("light");
519 break;
520 }
521 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
522 xfamily.c_str());
523 if ( wxTestFontSpec(fontSpec) )
524 {
525 xweight = wxT("thin");
526 break;
527 }
528 }
529 break;
530 case wxNORMAL:
531 {
532 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
533 xfamily.c_str());
534 if ( wxTestFontSpec(fontSpec) )
535 {
536 xweight = wxT("medium");
537 break;
538 }
539 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
540 xfamily.c_str());
541 if ( wxTestFontSpec(fontSpec) )
542 {
543 xweight = wxT("normal");
544 break;
545 }
546 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
547 xfamily.c_str());
548 if ( wxTestFontSpec(fontSpec) )
549 {
550 xweight = wxT("regular");
551 break;
552 }
553 xweight = wxT("*");
554 }
555 break;
556 default: xweight = wxT("*"); break;
557 }
558
559 // construct the X font spec from our data
560 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%d-*-*-*-*-%s-%s"),
561 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
562 pointSize, xregistry.c_str(), xencoding.c_str());
563
564 if( xFontName )
565 *xFontName = fontSpec;
566
567 return wxLoadFont(fontSpec);
568 }
569
570 // ----------------------------------------------------------------------------
571 // wxFontModule
572 // ----------------------------------------------------------------------------
573
574 class wxFontModule : public wxModule
575 {
576 public:
577 bool OnInit();
578 void OnExit();
579
580 private:
581 DECLARE_DYNAMIC_CLASS(wxFontModule)
582 };
583
584 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
585
586 bool wxFontModule::OnInit()
587 {
588 g_fontHash = new wxHashTable( wxKEY_STRING );
589
590 return TRUE;
591 }
592
593 void wxFontModule::OnExit()
594 {
595 delete g_fontHash;
596
597 g_fontHash = (wxHashTable *)NULL;
598 }