]> git.saurik.com Git - wxWidgets.git/blob - src/unix/fontutil.cpp
fix for a fatal bug in wxMGL's wxDir
[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 bool wxNativeFontInfo::FromXFontName(const wxString& xFontName)
200 {
201 // TODO: we should be able to handle the font aliases here, but how?
202 wxStringTokenizer tokenizer(xFontName, _T("-"), wxTOKEN_STRTOK);
203
204 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
205 {
206 if ( !tokenizer.HasMoreTokens() )
207 {
208 // not enough elements in the XLFD - or maybe an alias
209 return FALSE;
210 }
211
212 fontElements[n] = tokenizer.GetNextToken();
213 }
214
215 // this should be all
216 return !tokenizer.HasMoreTokens();
217 }
218
219 wxString wxNativeFontInfo::GetXFontName() const
220 {
221 if ( xFontName.empty() )
222 {
223 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
224 {
225 // replace the non specified elements with '*' except for the
226 // additional style which is usually just omitted
227 wxString elt = fontElements[n];
228 if ( elt.empty() && n != 5 )
229 {
230 elt = _T('*');
231 }
232
233 // const_cast
234 ((wxNativeFontInfo *)this)->xFontName << _T('-') << elt;
235 }
236 }
237
238 return xFontName;
239 }
240
241 // ----------------------------------------------------------------------------
242 // common functions
243 // ----------------------------------------------------------------------------
244
245 bool wxGetNativeFontEncoding(wxFontEncoding encoding,
246 wxNativeEncodingInfo *info)
247 {
248 wxCHECK_MSG( info, FALSE, _T("bad pointer in wxGetNativeFontEncoding") );
249
250 if ( encoding == wxFONTENCODING_DEFAULT )
251 {
252 encoding = wxFont::GetDefaultEncoding();
253 }
254
255 switch ( encoding )
256 {
257 case wxFONTENCODING_ISO8859_1:
258 case wxFONTENCODING_ISO8859_2:
259 case wxFONTENCODING_ISO8859_3:
260 case wxFONTENCODING_ISO8859_4:
261 case wxFONTENCODING_ISO8859_5:
262 case wxFONTENCODING_ISO8859_6:
263 case wxFONTENCODING_ISO8859_7:
264 case wxFONTENCODING_ISO8859_8:
265 case wxFONTENCODING_ISO8859_9:
266 case wxFONTENCODING_ISO8859_10:
267 case wxFONTENCODING_ISO8859_11:
268 case wxFONTENCODING_ISO8859_12:
269 case wxFONTENCODING_ISO8859_13:
270 case wxFONTENCODING_ISO8859_14:
271 case wxFONTENCODING_ISO8859_15:
272 {
273 int cp = encoding - wxFONTENCODING_ISO8859_1 + 1;
274 info->xregistry = wxT("iso8859");
275 info->xencoding.Printf(wxT("%d"), cp);
276 }
277 break;
278
279 case wxFONTENCODING_UTF8:
280 info->xregistry = wxT("iso10646");
281 info->xencoding = wxT("*");
282 break;
283
284 case wxFONTENCODING_KOI8:
285 info->xregistry = wxT("koi8");
286
287 // we don't make distinction between koi8-r, koi8-u and koi8-ru (so far)
288 info->xencoding = wxT("*");
289 break;
290
291 case wxFONTENCODING_CP1250:
292 case wxFONTENCODING_CP1251:
293 case wxFONTENCODING_CP1252:
294 case wxFONTENCODING_CP1253:
295 case wxFONTENCODING_CP1254:
296 case wxFONTENCODING_CP1255:
297 case wxFONTENCODING_CP1256:
298 case wxFONTENCODING_CP1257:
299 {
300 int cp = encoding - wxFONTENCODING_CP1250 + 1250;
301 info->xregistry = wxT("microsoft");
302 info->xencoding.Printf(wxT("cp%d"), cp);
303 }
304 break;
305
306 case wxFONTENCODING_SYSTEM:
307 info->xregistry =
308 info->xencoding = wxT("*");
309 break;
310
311 default:
312 // don't know how to translate this encoding into X fontspec
313 return FALSE;
314 }
315
316 info->encoding = encoding;
317
318 return TRUE;
319 }
320
321 bool wxTestFontEncoding(const wxNativeEncodingInfo& info)
322 {
323 wxString fontspec;
324 fontspec.Printf(_T("-*-%s-*-*-*-*-*-*-*-*-*-*-%s-%s"),
325 !info.facename ? _T("*") : info.facename.c_str(),
326 info.xregistry.c_str(),
327 info.xencoding.c_str());
328
329 return wxTestFontSpec(fontspec);
330 }
331
332 // ----------------------------------------------------------------------------
333 // X-specific functions
334 // ----------------------------------------------------------------------------
335
336 wxNativeFont wxLoadQueryNearestFont(int pointSize,
337 int family,
338 int style,
339 int weight,
340 bool underlined,
341 const wxString &facename,
342 wxFontEncoding encoding,
343 wxString* xFontName)
344 {
345 if ( encoding == wxFONTENCODING_DEFAULT )
346 {
347 encoding = wxFont::GetDefaultEncoding();
348 }
349
350 // first determine the encoding - if the font doesn't exist at all in this
351 // encoding, it's useless to do all other approximations (i.e. size,
352 // family &c don't matter much)
353 wxNativeEncodingInfo info;
354 if ( encoding == wxFONTENCODING_SYSTEM )
355 {
356 // This will always work so we don't test to save time
357 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
358 }
359 else
360 {
361 if ( !wxGetNativeFontEncoding(encoding, &info) ||
362 !wxTestFontEncoding(info) )
363 {
364 #if wxUSE_FONTMAP
365 if ( !wxTheFontMapper->GetAltForEncoding(encoding, &info) )
366 #endif // wxUSE_FONTMAP
367 {
368 // unspported encoding - replace it with the default
369 //
370 // NB: we can't just return 0 from here because wxGTK code doesn't
371 // check for it (i.e. it supposes that we'll always succeed),
372 // so it would provoke a crash
373 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
374 }
375 }
376 }
377
378 // OK, we have the correct xregistry/xencoding in info structure
379 wxNativeFont font = 0;
380
381 // if we already have the X font name, try to use it
382 if( xFontName && !xFontName->IsEmpty() )
383 {
384 //
385 // Make sure point size is correct for scale factor.
386 //
387 wxStringTokenizer tokenizer(*xFontName, _T("-"), wxTOKEN_RET_DELIMS);
388 wxString newFontName;
389
390 for(int i = 0; i < 8; i++)
391 newFontName += tokenizer.NextToken();
392
393 (void) tokenizer.NextToken();
394
395 newFontName += wxString::Format("%d-", pointSize);
396
397 while(tokenizer.HasMoreTokens())
398 newFontName += tokenizer.GetNextToken();
399
400 font = wxLoadFont(newFontName);
401
402 if(font)
403 *xFontName = newFontName;
404 }
405
406 // try to load exactly the font requested first
407 if( !font )
408 {
409 font = wxLoadQueryFont( pointSize, family, style, weight,
410 underlined, facename,
411 info.xregistry, info.xencoding,
412 xFontName );
413 }
414
415 if ( !font )
416 {
417 // search up and down by stepsize 10
418 int max_size = pointSize + 20 * (1 + (pointSize/180));
419 int min_size = pointSize - 20 * (1 + (pointSize/180));
420
421 int i;
422
423 // Search for smaller size (approx.)
424 for ( i = pointSize - 10; !font && i >= 10 && i >= min_size; i -= 10 )
425 {
426 font = wxLoadQueryFont(i, family, style, weight, underlined,
427 facename, info.xregistry, info.xencoding,
428 xFontName);
429 }
430
431 // Search for larger size (approx.)
432 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
433 {
434 font = wxLoadQueryFont(i, family, style, weight, underlined,
435 facename, info.xregistry, info.xencoding,
436 xFontName);
437 }
438
439 // Try default family
440 if ( !font && family != wxDEFAULT )
441 {
442 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
443 underlined, facename,
444 info.xregistry, info.xencoding,
445 xFontName );
446 }
447
448 // ignore size, family, style and weight but try to find font with the
449 // given facename and encoding
450 if ( !font )
451 {
452 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
453 underlined, facename,
454 info.xregistry, info.xencoding,
455 xFontName);
456
457 // ignore family as well
458 if ( !font )
459 {
460 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
461 underlined, wxEmptyString,
462 info.xregistry, info.xencoding,
463 xFontName);
464
465 // if it still failed, try to get the font of any size but
466 // with the requested encoding: this can happen if the
467 // encoding is only available in one size which happens to be
468 // different from 120
469 if ( !font )
470 {
471 font = wxLoadQueryFont(-1, wxDEFAULT, wxNORMAL, wxNORMAL,
472 FALSE, wxEmptyString,
473 info.xregistry, info.xencoding,
474 xFontName);
475
476 // this should never happen as we had tested for it in the
477 // very beginning, but if it does, do return something non
478 // NULL or we'd crash in wxFont code
479 if ( !font )
480 {
481 wxFAIL_MSG( _T("this encoding should be available!") );
482
483 font = wxLoadQueryFont(-1,
484 wxDEFAULT, wxNORMAL, wxNORMAL,
485 FALSE, wxEmptyString,
486 _T("*"), _T("*"),
487 xFontName);
488 }
489 }
490 }
491 }
492 }
493
494 return font;
495 }
496
497 // ----------------------------------------------------------------------------
498 // private functions
499 // ----------------------------------------------------------------------------
500
501 // returns TRUE if there are any fonts matching this font spec
502 static bool wxTestFontSpec(const wxString& fontspec)
503 {
504 // some X servers will fail to load this font because there are too many
505 // matches so we must test explicitly for this
506 if ( fontspec == _T("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
507 {
508 return TRUE;
509 }
510
511 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
512 if (test)
513 {
514 return TRUE;
515 }
516
517 test = wxLoadFont(fontspec);
518 g_fontHash->Put( fontspec, (wxObject*) test );
519
520 if ( test )
521 {
522 wxFreeFont(test);
523
524 return TRUE;
525 }
526 else
527 {
528 return FALSE;
529 }
530 }
531
532 static wxNativeFont wxLoadQueryFont(int pointSize,
533 int family,
534 int style,
535 int weight,
536 bool WXUNUSED(underlined),
537 const wxString& facename,
538 const wxString& xregistry,
539 const wxString& xencoding,
540 wxString* xFontName)
541 {
542 wxString xfamily;
543 switch (family)
544 {
545 case wxDECORATIVE: xfamily = wxT("lucida"); break;
546 case wxROMAN: xfamily = wxT("times"); break;
547 case wxMODERN: xfamily = wxT("courier"); break;
548 case wxSWISS: xfamily = wxT("helvetica"); break;
549 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
550 case wxSCRIPT: xfamily = wxT("utopia"); break;
551 default: xfamily = wxT("*");
552 }
553
554 wxString fontSpec;
555 if (!facename.IsEmpty())
556 {
557 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
558 facename.c_str());
559
560 if ( wxTestFontSpec(fontSpec) )
561 {
562 xfamily = facename;
563 }
564 //else: no such family, use default one instead
565 }
566
567 wxString xstyle;
568 switch (style)
569 {
570 case wxSLANT:
571 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
572 xfamily.c_str());
573 if ( wxTestFontSpec(fontSpec) )
574 {
575 xstyle = wxT("o");
576 break;
577 }
578 // fall through - try wxITALIC now
579
580 case wxITALIC:
581 fontSpec.Printf(wxT("-*-%s-*-i-*-*-*-*-*-*-*-*-*-*"),
582 xfamily.c_str());
583 if ( wxTestFontSpec(fontSpec) )
584 {
585 xstyle = wxT("i");
586 }
587 else if ( style == wxITALIC ) // and not wxSLANT
588 {
589 // try wxSLANT
590 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
591 xfamily.c_str());
592 if ( wxTestFontSpec(fontSpec) )
593 {
594 xstyle = wxT("o");
595 }
596 else
597 {
598 // no italic, no slant - leave default
599 xstyle = wxT("*");
600 }
601 }
602 break;
603
604 default:
605 wxFAIL_MSG(_T("unknown font style"));
606 // fall back to normal
607
608 case wxNORMAL:
609 xstyle = wxT("r");
610 break;
611 }
612
613 wxString xweight;
614 switch (weight)
615 {
616 case wxBOLD:
617 {
618 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
619 xfamily.c_str());
620 if ( wxTestFontSpec(fontSpec) )
621 {
622 xweight = wxT("bold");
623 break;
624 }
625 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
626 xfamily.c_str());
627 if ( wxTestFontSpec(fontSpec) )
628 {
629 xweight = wxT("heavy");
630 break;
631 }
632 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
633 xfamily.c_str());
634 if ( wxTestFontSpec(fontSpec) )
635 {
636 xweight = wxT("extrabold");
637 break;
638 }
639 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
640 xfamily.c_str());
641 if ( wxTestFontSpec(fontSpec) )
642 {
643 xweight = wxT("demibold");
644 break;
645 }
646 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
647 xfamily.c_str());
648 if ( wxTestFontSpec(fontSpec) )
649 {
650 xweight = wxT("black");
651 break;
652 }
653 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
654 xfamily.c_str());
655 if ( wxTestFontSpec(fontSpec) )
656 {
657 xweight = wxT("ultrablack");
658 break;
659 }
660 }
661 break;
662 case wxLIGHT:
663 {
664 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
665 xfamily.c_str());
666 if ( wxTestFontSpec(fontSpec) )
667 {
668 xweight = wxT("light");
669 break;
670 }
671 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
672 xfamily.c_str());
673 if ( wxTestFontSpec(fontSpec) )
674 {
675 xweight = wxT("thin");
676 break;
677 }
678 }
679 break;
680 case wxNORMAL:
681 {
682 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
683 xfamily.c_str());
684 if ( wxTestFontSpec(fontSpec) )
685 {
686 xweight = wxT("medium");
687 break;
688 }
689 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
690 xfamily.c_str());
691 if ( wxTestFontSpec(fontSpec) )
692 {
693 xweight = wxT("normal");
694 break;
695 }
696 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
697 xfamily.c_str());
698 if ( wxTestFontSpec(fontSpec) )
699 {
700 xweight = wxT("regular");
701 break;
702 }
703 xweight = wxT("*");
704 }
705 break;
706 default: xweight = wxT("*"); break;
707 }
708
709 // if pointSize is -1, don't specify any
710 wxString sizeSpec;
711 if ( fontSpec == -1 )
712 {
713 sizeSpec = _T('*');
714 }
715 else
716 {
717 sizeSpec.Printf(_T("%d"), pointSize);
718 }
719
720 // construct the X font spec from our data
721 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%s-*-*-*-*-%s-%s"),
722 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
723 sizeSpec.c_str(), xregistry.c_str(), xencoding.c_str());
724
725 if( xFontName )
726 *xFontName = fontSpec;
727
728 return wxLoadFont(fontSpec);
729 }
730
731 // ----------------------------------------------------------------------------
732 // wxFontModule
733 // ----------------------------------------------------------------------------
734
735 class wxFontModule : public wxModule
736 {
737 public:
738 bool OnInit();
739 void OnExit();
740
741 private:
742 DECLARE_DYNAMIC_CLASS(wxFontModule)
743 };
744
745 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
746
747 bool wxFontModule::OnInit()
748 {
749 g_fontHash = new wxHashTable( wxKEY_STRING );
750
751 return TRUE;
752 }
753
754 void wxFontModule::OnExit()
755 {
756 delete g_fontHash;
757
758 g_fontHash = (wxHashTable *)NULL;
759 }