applied Derry's patch to fix scaling bug
[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 // use ";", not "-" because it may be part of encoding name
114 wxStringTokenizer tokenizer(s, _T(";"));
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 {
284 //
285 // Make sure point size is correct for scale factor.
286 //
287 wxStringTokenizer tokenizer(*xFontName, _T("-"), wxTOKEN_RET_DELIMS);
288 wxString newFontName;
289
290 for(int i = 0; i < 8; i++)
291 newFontName += tokenizer.NextToken();
292
293 (void) tokenizer.NextToken();
294
295 newFontName += wxString::Format("%d-", pointSize);
296
297 while(tokenizer.HasMoreTokens())
298 newFontName += tokenizer.GetNextToken();
299
300 font = wxLoadFont(newFontName);
301
302 if(font)
303 *xFontName = newFontName;
304 }
305
306 if( !font )
307 font = wxLoadQueryFont( pointSize, family, style, weight,
308 underlined, facename,
309 info.xregistry, info.xencoding,
310 xFontName );
311
312 if ( !font )
313 {
314 // search up and down by stepsize 10
315 int max_size = pointSize + 20 * (1 + (pointSize/180));
316 int min_size = pointSize - 20 * (1 + (pointSize/180));
317
318 int i;
319
320 // Search for smaller size (approx.)
321 for ( i = pointSize - 10; !font && i >= 10 && i >= min_size; i -= 10 )
322 {
323 font = wxLoadQueryFont(i, family, style, weight, underlined,
324 facename, info.xregistry, info.xencoding,
325 xFontName);
326 }
327
328 // Search for larger size (approx.)
329 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
330 {
331 font = wxLoadQueryFont(i, family, style, weight, underlined,
332 facename, info.xregistry, info.xencoding,
333 xFontName);
334 }
335
336 // Try default family
337 if ( !font && family != wxDEFAULT )
338 {
339 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
340 underlined, facename,
341 info.xregistry, info.xencoding,
342 xFontName );
343 }
344
345 // Bogus font I
346 if ( !font )
347 {
348 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
349 underlined, facename,
350 info.xregistry, info.xencoding,
351 xFontName);
352 }
353
354 // Bogus font II
355 if ( !font )
356 {
357 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
358 underlined, wxEmptyString,
359 info.xregistry, info.xencoding,
360 xFontName);
361 }
362 }
363
364 return font;
365 }
366
367 // ----------------------------------------------------------------------------
368 // private functions
369 // ----------------------------------------------------------------------------
370
371 // returns TRUE if there are any fonts matching this font spec
372 static bool wxTestFontSpec(const wxString& fontspec)
373 {
374 // some X servers will fail to load this font because there are too many
375 // matches so we must test explicitly for this
376 if ( fontspec == _T("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
377 {
378 return TRUE;
379 }
380
381 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
382 if (test)
383 {
384 return TRUE;
385 }
386
387 test = wxLoadFont(fontspec);
388 g_fontHash->Put( fontspec, (wxObject*) test );
389
390 if ( test )
391 {
392 wxFreeFont(test);
393
394 return TRUE;
395 }
396 else
397 {
398 return FALSE;
399 }
400 }
401
402 static wxNativeFont wxLoadQueryFont(int pointSize,
403 int family,
404 int style,
405 int weight,
406 bool WXUNUSED(underlined),
407 const wxString& facename,
408 const wxString& xregistry,
409 const wxString& xencoding,
410 wxString* xFontName)
411 {
412 wxString xfamily;
413 switch (family)
414 {
415 case wxDECORATIVE: xfamily = wxT("lucida"); break;
416 case wxROMAN: xfamily = wxT("times"); break;
417 case wxMODERN: xfamily = wxT("courier"); break;
418 case wxSWISS: xfamily = wxT("helvetica"); break;
419 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
420 case wxSCRIPT: xfamily = wxT("utopia"); break;
421 default: xfamily = wxT("*");
422 }
423
424 wxString fontSpec;
425 if (!facename.IsEmpty())
426 {
427 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
428 facename.c_str());
429
430 if ( wxTestFontSpec(fontSpec) )
431 {
432 xfamily = facename;
433 }
434 //else: no such family, use default one instead
435 }
436
437 wxString xstyle;
438 switch (style)
439 {
440 case wxSLANT:
441 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
442 xfamily.c_str());
443 if ( wxTestFontSpec(fontSpec) )
444 {
445 xstyle = wxT("o");
446 break;
447 }
448 // fall through - try wxITALIC now
449
450 case wxITALIC:
451 fontSpec.Printf(wxT("-*-%s-*-i-*-*-*-*-*-*-*-*-*-*"),
452 xfamily.c_str());
453 if ( wxTestFontSpec(fontSpec) )
454 {
455 xstyle = wxT("i");
456 }
457 else if ( style == wxITALIC ) // and not wxSLANT
458 {
459 // try wxSLANT
460 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
461 xfamily.c_str());
462 if ( wxTestFontSpec(fontSpec) )
463 {
464 xstyle = wxT("o");
465 }
466 else
467 {
468 // no italic, no slant - leave default
469 xstyle = wxT("*");
470 }
471 }
472 break;
473
474 default:
475 wxFAIL_MSG(_T("unknown font style"));
476 // fall back to normal
477
478 case wxNORMAL:
479 xstyle = wxT("r");
480 break;
481 }
482
483 wxString xweight;
484 switch (weight)
485 {
486 case wxBOLD:
487 {
488 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
489 xfamily.c_str());
490 if ( wxTestFontSpec(fontSpec) )
491 {
492 xweight = wxT("bold");
493 break;
494 }
495 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
496 xfamily.c_str());
497 if ( wxTestFontSpec(fontSpec) )
498 {
499 xweight = wxT("heavy");
500 break;
501 }
502 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
503 xfamily.c_str());
504 if ( wxTestFontSpec(fontSpec) )
505 {
506 xweight = wxT("extrabold");
507 break;
508 }
509 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
510 xfamily.c_str());
511 if ( wxTestFontSpec(fontSpec) )
512 {
513 xweight = wxT("demibold");
514 break;
515 }
516 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
517 xfamily.c_str());
518 if ( wxTestFontSpec(fontSpec) )
519 {
520 xweight = wxT("black");
521 break;
522 }
523 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
524 xfamily.c_str());
525 if ( wxTestFontSpec(fontSpec) )
526 {
527 xweight = wxT("ultrablack");
528 break;
529 }
530 }
531 break;
532 case wxLIGHT:
533 {
534 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
535 xfamily.c_str());
536 if ( wxTestFontSpec(fontSpec) )
537 {
538 xweight = wxT("light");
539 break;
540 }
541 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
542 xfamily.c_str());
543 if ( wxTestFontSpec(fontSpec) )
544 {
545 xweight = wxT("thin");
546 break;
547 }
548 }
549 break;
550 case wxNORMAL:
551 {
552 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
553 xfamily.c_str());
554 if ( wxTestFontSpec(fontSpec) )
555 {
556 xweight = wxT("medium");
557 break;
558 }
559 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
560 xfamily.c_str());
561 if ( wxTestFontSpec(fontSpec) )
562 {
563 xweight = wxT("normal");
564 break;
565 }
566 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
567 xfamily.c_str());
568 if ( wxTestFontSpec(fontSpec) )
569 {
570 xweight = wxT("regular");
571 break;
572 }
573 xweight = wxT("*");
574 }
575 break;
576 default: xweight = wxT("*"); break;
577 }
578
579 // construct the X font spec from our data
580 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%d-*-*-*-*-%s-%s"),
581 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
582 pointSize, xregistry.c_str(), xencoding.c_str());
583
584 if( xFontName )
585 *xFontName = fontSpec;
586
587 return wxLoadFont(fontSpec);
588 }
589
590 // ----------------------------------------------------------------------------
591 // wxFontModule
592 // ----------------------------------------------------------------------------
593
594 class wxFontModule : public wxModule
595 {
596 public:
597 bool OnInit();
598 void OnExit();
599
600 private:
601 DECLARE_DYNAMIC_CLASS(wxFontModule)
602 };
603
604 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
605
606 bool wxFontModule::OnInit()
607 {
608 g_fontHash = new wxHashTable( wxKEY_STRING );
609
610 return TRUE;
611 }
612
613 void wxFontModule::OnExit()
614 {
615 delete g_fontHash;
616
617 g_fontHash = (wxHashTable *)NULL;
618 }