added wxFont::IsFixedWidth(), documented it and implemented for wxGTK/Motif
[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
39 #include <X11/Xlib.h>
40
41 #ifdef __VMS__
42 #pragma message enable nosimpint
43 #endif
44
45 #include "wx/utils.h" // for wxGetDisplay()
46 #elif defined(__WXGTK__)
47 // we have to declare struct tm to avoid problems with first forward
48 // declaring it in C code (glib.h included from gdk.h does it) and then
49 // defining it when time.h is included from the headers below - this is
50 // known not to work at least with Sun CC 6.01
51 #include <time.h>
52
53 #include <gdk/gdk.h>
54 #endif
55
56 #include "wx/fontutil.h"
57 #include "wx/fontmap.h"
58 #include "wx/tokenzr.h"
59 #include "wx/hash.h"
60 #include "wx/module.h"
61
62 // ----------------------------------------------------------------------------
63 // private data
64 // ----------------------------------------------------------------------------
65
66 static wxHashTable *g_fontHash = (wxHashTable*) NULL;
67
68 // ----------------------------------------------------------------------------
69 // private functions
70 // ----------------------------------------------------------------------------
71
72 // define the functions to create and destroy native fonts for this toolkit
73 #ifdef __X__
74 static inline wxNativeFont wxLoadFont(const wxString& fontSpec)
75 {
76 return XLoadQueryFont((Display *)wxGetDisplay(), fontSpec);
77 }
78
79 static inline void wxFreeFont(wxNativeFont font)
80 {
81 XFreeFont((Display *)wxGetDisplay(), (XFontStruct *)font);
82 }
83 #elif defined(__WXGTK__)
84 static inline wxNativeFont wxLoadFont(const wxString& fontSpec)
85 {
86 return gdk_font_load( wxConvertWX2MB(fontSpec) );
87 }
88
89 static inline void wxFreeFont(wxNativeFont font)
90 {
91 gdk_font_unref(font);
92 }
93 #else
94 #error "Unknown GUI toolkit"
95 #endif
96
97 static bool wxTestFontSpec(const wxString& fontspec);
98
99 static wxNativeFont wxLoadQueryFont(int pointSize,
100 int family,
101 int style,
102 int weight,
103 bool underlined,
104 const wxString& facename,
105 const wxString& xregistry,
106 const wxString& xencoding,
107 wxString* xFontName);
108
109 // ============================================================================
110 // implementation
111 // ============================================================================
112
113 // ----------------------------------------------------------------------------
114 // wxNativeEncodingInfo
115 // ----------------------------------------------------------------------------
116
117 // convert to/from the string representation: format is
118 // encodingid;registry;encoding[;facename]
119 bool wxNativeEncodingInfo::FromString(const wxString& s)
120 {
121 // use ";", not "-" because it may be part of encoding name
122 wxStringTokenizer tokenizer(s, _T(";"));
123
124 wxString encid = tokenizer.GetNextToken();
125 long enc;
126 if ( !encid.ToLong(&enc) )
127 return FALSE;
128 encoding = (wxFontEncoding)enc;
129
130 xregistry = tokenizer.GetNextToken();
131 if ( !xregistry )
132 return FALSE;
133
134 xencoding = tokenizer.GetNextToken();
135 if ( !xencoding )
136 return FALSE;
137
138 // ok even if empty
139 facename = tokenizer.GetNextToken();
140
141 return TRUE;
142 }
143
144 wxString wxNativeEncodingInfo::ToString() const
145 {
146 wxString s;
147 s << (long)encoding << _T(';') << xregistry << _T(';') << xencoding;
148 if ( !!facename )
149 {
150 s << _T(';') << facename;
151 }
152
153 return s;
154 }
155
156 // ----------------------------------------------------------------------------
157 // wxNativeFontInfo
158 // ----------------------------------------------------------------------------
159
160 void wxNativeFontInfo::Init()
161 {
162 xFontName.clear();
163 }
164
165 bool wxNativeFontInfo::FromString(const wxString& s)
166 {
167 wxStringTokenizer tokenizer(s, _T(";"));
168
169 // check the version
170 wxString token = tokenizer.GetNextToken();
171 if ( token != _T('0') )
172 return FALSE;
173
174 xFontName = tokenizer.GetNextToken();
175
176 // this should be the end
177 if ( tokenizer.HasMoreTokens() )
178 return FALSE;
179
180 return FromXFontName(xFontName);
181 }
182
183 wxString wxNativeFontInfo::ToString() const
184 {
185 // 0 is the version
186 return wxString::Format(_T("%d;%s"), 0, GetXFontName().c_str());
187 }
188
189 bool wxNativeFontInfo::FromUserString(const wxString& s)
190 {
191 return FromXFontName(s);
192 }
193
194 wxString wxNativeFontInfo::ToUserString() const
195 {
196 return GetXFontName();
197 }
198
199 wxString wxNativeFontInfo::GetXFontComponent(wxXLFDField field) const
200 {
201 wxCHECK_MSG( field < wxXLFD_MAX, _T(""), _T("invalid XLFD field") );
202
203 if ( fontElements[0].empty() )
204 {
205 // const_cast
206 if ( !((wxNativeFontInfo *)this)->FromXFontName(xFontName) )
207 return _T("");
208 }
209
210 return fontElements[field];
211 }
212
213 bool wxNativeFontInfo::FromXFontName(const wxString& fontname)
214 {
215 // TODO: we should be able to handle the font aliases here, but how?
216 wxStringTokenizer tokenizer(fontname, _T("-"), wxTOKEN_STRTOK);
217
218 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
219 {
220 if ( !tokenizer.HasMoreTokens() )
221 {
222 // not enough elements in the XLFD - or maybe an alias
223 return FALSE;
224 }
225
226 fontElements[n] = tokenizer.GetNextToken();
227 }
228
229 // this should be all
230 return !tokenizer.HasMoreTokens();
231 }
232
233 wxString wxNativeFontInfo::GetXFontName() const
234 {
235 if ( xFontName.empty() )
236 {
237 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
238 {
239 // replace the non specified elements with '*' except for the
240 // additional style which is usually just omitted
241 wxString elt = fontElements[n];
242 if ( elt.empty() && n != 5 )
243 {
244 elt = _T('*');
245 }
246
247 // const_cast
248 ((wxNativeFontInfo *)this)->xFontName << _T('-') << elt;
249 }
250 }
251
252 return xFontName;
253 }
254
255 // ----------------------------------------------------------------------------
256 // common functions
257 // ----------------------------------------------------------------------------
258
259 bool wxGetNativeFontEncoding(wxFontEncoding encoding,
260 wxNativeEncodingInfo *info)
261 {
262 wxCHECK_MSG( info, FALSE, _T("bad pointer in wxGetNativeFontEncoding") );
263
264 if ( encoding == wxFONTENCODING_DEFAULT )
265 {
266 encoding = wxFont::GetDefaultEncoding();
267 }
268
269 switch ( encoding )
270 {
271 case wxFONTENCODING_ISO8859_1:
272 case wxFONTENCODING_ISO8859_2:
273 case wxFONTENCODING_ISO8859_3:
274 case wxFONTENCODING_ISO8859_4:
275 case wxFONTENCODING_ISO8859_5:
276 case wxFONTENCODING_ISO8859_6:
277 case wxFONTENCODING_ISO8859_7:
278 case wxFONTENCODING_ISO8859_8:
279 case wxFONTENCODING_ISO8859_9:
280 case wxFONTENCODING_ISO8859_10:
281 case wxFONTENCODING_ISO8859_11:
282 case wxFONTENCODING_ISO8859_12:
283 case wxFONTENCODING_ISO8859_13:
284 case wxFONTENCODING_ISO8859_14:
285 case wxFONTENCODING_ISO8859_15:
286 {
287 int cp = encoding - wxFONTENCODING_ISO8859_1 + 1;
288 info->xregistry = wxT("iso8859");
289 info->xencoding.Printf(wxT("%d"), cp);
290 }
291 break;
292
293 case wxFONTENCODING_UTF8:
294 info->xregistry = wxT("iso10646");
295 info->xencoding = wxT("*");
296 break;
297
298 case wxFONTENCODING_KOI8:
299 info->xregistry = wxT("koi8");
300
301 // we don't make distinction between koi8-r, koi8-u and koi8-ru (so far)
302 info->xencoding = wxT("*");
303 break;
304
305 case wxFONTENCODING_CP1250:
306 case wxFONTENCODING_CP1251:
307 case wxFONTENCODING_CP1252:
308 case wxFONTENCODING_CP1253:
309 case wxFONTENCODING_CP1254:
310 case wxFONTENCODING_CP1255:
311 case wxFONTENCODING_CP1256:
312 case wxFONTENCODING_CP1257:
313 {
314 int cp = encoding - wxFONTENCODING_CP1250 + 1250;
315 info->xregistry = wxT("microsoft");
316 info->xencoding.Printf(wxT("cp%d"), cp);
317 }
318 break;
319
320 case wxFONTENCODING_SYSTEM:
321 info->xregistry =
322 info->xencoding = wxT("*");
323 break;
324
325 default:
326 // don't know how to translate this encoding into X fontspec
327 return FALSE;
328 }
329
330 info->encoding = encoding;
331
332 return TRUE;
333 }
334
335 bool wxTestFontEncoding(const wxNativeEncodingInfo& info)
336 {
337 wxString fontspec;
338 fontspec.Printf(_T("-*-%s-*-*-*-*-*-*-*-*-*-*-%s-%s"),
339 !info.facename ? _T("*") : info.facename.c_str(),
340 info.xregistry.c_str(),
341 info.xencoding.c_str());
342
343 return wxTestFontSpec(fontspec);
344 }
345
346 // ----------------------------------------------------------------------------
347 // X-specific functions
348 // ----------------------------------------------------------------------------
349
350 wxNativeFont wxLoadQueryNearestFont(int pointSize,
351 int family,
352 int style,
353 int weight,
354 bool underlined,
355 const wxString &facename,
356 wxFontEncoding encoding,
357 wxString* xFontName)
358 {
359 if ( encoding == wxFONTENCODING_DEFAULT )
360 {
361 encoding = wxFont::GetDefaultEncoding();
362 }
363
364 // first determine the encoding - if the font doesn't exist at all in this
365 // encoding, it's useless to do all other approximations (i.e. size,
366 // family &c don't matter much)
367 wxNativeEncodingInfo info;
368 if ( encoding == wxFONTENCODING_SYSTEM )
369 {
370 // This will always work so we don't test to save time
371 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
372 }
373 else
374 {
375 if ( !wxGetNativeFontEncoding(encoding, &info) ||
376 !wxTestFontEncoding(info) )
377 {
378 #if wxUSE_FONTMAP
379 if ( !wxTheFontMapper->GetAltForEncoding(encoding, &info) )
380 #endif // wxUSE_FONTMAP
381 {
382 // unspported encoding - replace it with the default
383 //
384 // NB: we can't just return 0 from here because wxGTK code doesn't
385 // check for it (i.e. it supposes that we'll always succeed),
386 // so it would provoke a crash
387 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
388 }
389 }
390 }
391
392 // OK, we have the correct xregistry/xencoding in info structure
393 wxNativeFont font = 0;
394
395 // if we already have the X font name, try to use it
396 if( xFontName && !xFontName->IsEmpty() )
397 {
398 //
399 // Make sure point size is correct for scale factor.
400 //
401 wxStringTokenizer tokenizer(*xFontName, _T("-"), wxTOKEN_RET_DELIMS);
402 wxString newFontName;
403
404 for(int i = 0; i < 8; i++)
405 newFontName += tokenizer.NextToken();
406
407 (void) tokenizer.NextToken();
408
409 newFontName += wxString::Format("%d-", pointSize);
410
411 while(tokenizer.HasMoreTokens())
412 newFontName += tokenizer.GetNextToken();
413
414 font = wxLoadFont(newFontName);
415
416 if(font)
417 *xFontName = newFontName;
418 }
419
420 // try to load exactly the font requested first
421 if( !font )
422 {
423 font = wxLoadQueryFont( pointSize, family, style, weight,
424 underlined, facename,
425 info.xregistry, info.xencoding,
426 xFontName );
427 }
428
429 if ( !font )
430 {
431 // search up and down by stepsize 10
432 int max_size = pointSize + 20 * (1 + (pointSize/180));
433 int min_size = pointSize - 20 * (1 + (pointSize/180));
434
435 int i;
436
437 // Search for smaller size (approx.)
438 for ( i = pointSize - 10; !font && i >= 10 && i >= min_size; i -= 10 )
439 {
440 font = wxLoadQueryFont(i, family, style, weight, underlined,
441 facename, info.xregistry, info.xencoding,
442 xFontName);
443 }
444
445 // Search for larger size (approx.)
446 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
447 {
448 font = wxLoadQueryFont(i, family, style, weight, underlined,
449 facename, info.xregistry, info.xencoding,
450 xFontName);
451 }
452
453 // Try default family
454 if ( !font && family != wxDEFAULT )
455 {
456 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
457 underlined, facename,
458 info.xregistry, info.xencoding,
459 xFontName );
460 }
461
462 // ignore size, family, style and weight but try to find font with the
463 // given facename and encoding
464 if ( !font )
465 {
466 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
467 underlined, facename,
468 info.xregistry, info.xencoding,
469 xFontName);
470
471 // ignore family as well
472 if ( !font )
473 {
474 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
475 underlined, wxEmptyString,
476 info.xregistry, info.xencoding,
477 xFontName);
478
479 // if it still failed, try to get the font of any size but
480 // with the requested encoding: this can happen if the
481 // encoding is only available in one size which happens to be
482 // different from 120
483 if ( !font )
484 {
485 font = wxLoadQueryFont(-1, wxDEFAULT, wxNORMAL, wxNORMAL,
486 FALSE, wxEmptyString,
487 info.xregistry, info.xencoding,
488 xFontName);
489
490 // this should never happen as we had tested for it in the
491 // very beginning, but if it does, do return something non
492 // NULL or we'd crash in wxFont code
493 if ( !font )
494 {
495 wxFAIL_MSG( _T("this encoding should be available!") );
496
497 font = wxLoadQueryFont(-1,
498 wxDEFAULT, wxNORMAL, wxNORMAL,
499 FALSE, wxEmptyString,
500 _T("*"), _T("*"),
501 xFontName);
502 }
503 }
504 }
505 }
506 }
507
508 return font;
509 }
510
511 // ----------------------------------------------------------------------------
512 // private functions
513 // ----------------------------------------------------------------------------
514
515 // returns TRUE if there are any fonts matching this font spec
516 static bool wxTestFontSpec(const wxString& fontspec)
517 {
518 // some X servers will fail to load this font because there are too many
519 // matches so we must test explicitly for this
520 if ( fontspec == _T("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
521 {
522 return TRUE;
523 }
524
525 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
526 if (test)
527 {
528 return TRUE;
529 }
530
531 test = wxLoadFont(fontspec);
532 g_fontHash->Put( fontspec, (wxObject*) test );
533
534 if ( test )
535 {
536 wxFreeFont(test);
537
538 return TRUE;
539 }
540 else
541 {
542 return FALSE;
543 }
544 }
545
546 static wxNativeFont wxLoadQueryFont(int pointSize,
547 int family,
548 int style,
549 int weight,
550 bool WXUNUSED(underlined),
551 const wxString& facename,
552 const wxString& xregistry,
553 const wxString& xencoding,
554 wxString* xFontName)
555 {
556 wxString xfamily;
557 switch (family)
558 {
559 case wxDECORATIVE: xfamily = wxT("lucida"); break;
560 case wxROMAN: xfamily = wxT("times"); break;
561 case wxMODERN: xfamily = wxT("courier"); break;
562 case wxSWISS: xfamily = wxT("helvetica"); break;
563 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
564 case wxSCRIPT: xfamily = wxT("utopia"); break;
565 default: xfamily = wxT("*");
566 }
567
568 wxString fontSpec;
569 if (!facename.IsEmpty())
570 {
571 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
572 facename.c_str());
573
574 if ( wxTestFontSpec(fontSpec) )
575 {
576 xfamily = facename;
577 }
578 //else: no such family, use default one instead
579 }
580
581 wxString xstyle;
582 switch (style)
583 {
584 case wxSLANT:
585 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
586 xfamily.c_str());
587 if ( wxTestFontSpec(fontSpec) )
588 {
589 xstyle = wxT("o");
590 break;
591 }
592 // fall through - try wxITALIC now
593
594 case wxITALIC:
595 fontSpec.Printf(wxT("-*-%s-*-i-*-*-*-*-*-*-*-*-*-*"),
596 xfamily.c_str());
597 if ( wxTestFontSpec(fontSpec) )
598 {
599 xstyle = wxT("i");
600 }
601 else if ( style == wxITALIC ) // and not wxSLANT
602 {
603 // try wxSLANT
604 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
605 xfamily.c_str());
606 if ( wxTestFontSpec(fontSpec) )
607 {
608 xstyle = wxT("o");
609 }
610 else
611 {
612 // no italic, no slant - leave default
613 xstyle = wxT("*");
614 }
615 }
616 break;
617
618 default:
619 wxFAIL_MSG(_T("unknown font style"));
620 // fall back to normal
621
622 case wxNORMAL:
623 xstyle = wxT("r");
624 break;
625 }
626
627 wxString xweight;
628 switch (weight)
629 {
630 case wxBOLD:
631 {
632 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
633 xfamily.c_str());
634 if ( wxTestFontSpec(fontSpec) )
635 {
636 xweight = wxT("bold");
637 break;
638 }
639 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
640 xfamily.c_str());
641 if ( wxTestFontSpec(fontSpec) )
642 {
643 xweight = wxT("heavy");
644 break;
645 }
646 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
647 xfamily.c_str());
648 if ( wxTestFontSpec(fontSpec) )
649 {
650 xweight = wxT("extrabold");
651 break;
652 }
653 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
654 xfamily.c_str());
655 if ( wxTestFontSpec(fontSpec) )
656 {
657 xweight = wxT("demibold");
658 break;
659 }
660 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
661 xfamily.c_str());
662 if ( wxTestFontSpec(fontSpec) )
663 {
664 xweight = wxT("black");
665 break;
666 }
667 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
668 xfamily.c_str());
669 if ( wxTestFontSpec(fontSpec) )
670 {
671 xweight = wxT("ultrablack");
672 break;
673 }
674 }
675 break;
676 case wxLIGHT:
677 {
678 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
679 xfamily.c_str());
680 if ( wxTestFontSpec(fontSpec) )
681 {
682 xweight = wxT("light");
683 break;
684 }
685 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
686 xfamily.c_str());
687 if ( wxTestFontSpec(fontSpec) )
688 {
689 xweight = wxT("thin");
690 break;
691 }
692 }
693 break;
694 case wxNORMAL:
695 {
696 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
697 xfamily.c_str());
698 if ( wxTestFontSpec(fontSpec) )
699 {
700 xweight = wxT("medium");
701 break;
702 }
703 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
704 xfamily.c_str());
705 if ( wxTestFontSpec(fontSpec) )
706 {
707 xweight = wxT("normal");
708 break;
709 }
710 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
711 xfamily.c_str());
712 if ( wxTestFontSpec(fontSpec) )
713 {
714 xweight = wxT("regular");
715 break;
716 }
717 xweight = wxT("*");
718 }
719 break;
720 default: xweight = wxT("*"); break;
721 }
722
723 // if pointSize is -1, don't specify any
724 wxString sizeSpec;
725 if ( fontSpec == -1 )
726 {
727 sizeSpec = _T('*');
728 }
729 else
730 {
731 sizeSpec.Printf(_T("%d"), pointSize);
732 }
733
734 // construct the X font spec from our data
735 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%s-*-*-*-*-%s-%s"),
736 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
737 sizeSpec.c_str(), xregistry.c_str(), xencoding.c_str());
738
739 if( xFontName )
740 *xFontName = fontSpec;
741
742 return wxLoadFont(fontSpec);
743 }
744
745 // ----------------------------------------------------------------------------
746 // wxFontModule
747 // ----------------------------------------------------------------------------
748
749 class wxFontModule : public wxModule
750 {
751 public:
752 bool OnInit();
753 void OnExit();
754
755 private:
756 DECLARE_DYNAMIC_CLASS(wxFontModule)
757 };
758
759 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
760
761 bool wxFontModule::OnInit()
762 {
763 g_fontHash = new wxHashTable( wxKEY_STRING );
764
765 return TRUE;
766 }
767
768 void wxFontModule::OnExit()
769 {
770 delete g_fontHash;
771
772 g_fontHash = (wxHashTable *)NULL;
773 }