1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/fontmap.cpp
3 // Purpose: wxFontMapper class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "fontmap.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
37 #include "wx/fontmap.h"
40 #include "wx/config.h"
41 #include "wx/memconf.h"
45 #include "wx/fontutil.h"
46 #include "wx/msgdlg.h"
47 #include "wx/fontdlg.h"
48 #include "wx/choicdlg.h"
51 #include "wx/encconv.h"
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 // the config paths we use
58 static const wxChar
* FONTMAPPER_ROOT_PATH
= wxT("/wxWindows/FontMapper");
59 static const wxChar
* FONTMAPPER_CHARSET_PATH
= wxT("Charsets");
60 static const wxChar
* FONTMAPPER_CHARSET_ALIAS_PATH
= wxT("Aliases");
62 static const wxChar
* FONTMAPPER_FONT_FROM_ENCODING_PATH
= wxT("Encodings");
65 // encodings supported by GetEncodingDescription
66 static wxFontEncoding gs_encodings
[] =
68 wxFONTENCODING_ISO8859_1
,
69 wxFONTENCODING_ISO8859_2
,
70 wxFONTENCODING_ISO8859_3
,
71 wxFONTENCODING_ISO8859_4
,
72 wxFONTENCODING_ISO8859_5
,
73 wxFONTENCODING_ISO8859_6
,
74 wxFONTENCODING_ISO8859_7
,
75 wxFONTENCODING_ISO8859_8
,
76 wxFONTENCODING_ISO8859_9
,
77 wxFONTENCODING_ISO8859_10
,
78 wxFONTENCODING_ISO8859_11
,
79 wxFONTENCODING_ISO8859_12
,
80 wxFONTENCODING_ISO8859_13
,
81 wxFONTENCODING_ISO8859_14
,
82 wxFONTENCODING_ISO8859_15
,
84 wxFONTENCODING_CP1250
,
85 wxFONTENCODING_CP1251
,
86 wxFONTENCODING_CP1252
,
87 wxFONTENCODING_CP1253
,
88 wxFONTENCODING_CP1254
,
89 wxFONTENCODING_CP1255
,
90 wxFONTENCODING_CP1256
,
91 wxFONTENCODING_CP1257
,
97 // the descriptions for them
98 static const wxChar
* gs_encodingDescs
[] =
100 wxTRANSLATE( "Western European (ISO-8859-1)" ),
101 wxTRANSLATE( "Central European (ISO-8859-2)" ),
102 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
103 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
104 wxTRANSLATE( "Cyrillic (ISO-8859-5)" ),
105 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
106 wxTRANSLATE( "Greek (ISO-8859-7)" ),
107 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
108 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
109 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
110 wxTRANSLATE( "Thai (ISO-8859-11)" ),
111 wxTRANSLATE( "Indian (ISO-8859-12)" ),
112 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
113 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
114 wxTRANSLATE( "Western European with Euro (ISO-8859-15)" ),
115 wxTRANSLATE( "KOI8-R" ),
116 wxTRANSLATE( "Windows Central European (CP 1250)" ),
117 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
118 wxTRANSLATE( "Windows Western European (CP 1252)" ),
119 wxTRANSLATE( "Windows Greek (CP 1253)" ),
120 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
121 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
122 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
123 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
124 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
125 wxTRANSLATE( "Unicode 7 bit (UTF-7)" ),
126 wxTRANSLATE( "Unicode 8 bit (UTF-8)" ),
129 // and the internal names
130 static const wxChar
* gs_encodingNames
[] =
148 wxT( "windows-1250" ),
149 wxT( "windows-1251" ),
150 wxT( "windows-1252" ),
151 wxT( "windows-1253" ),
152 wxT( "windows-1254" ),
153 wxT( "windows-1255" ),
154 wxT( "windows-1256" ),
155 wxT( "windows-1257" ),
156 wxT( "windows-437" ),
161 // ----------------------------------------------------------------------------
163 // ----------------------------------------------------------------------------
166 static wxFontMapper gs_fontMapper
;
168 // and public pointer
169 wxFontMapper
* wxTheFontMapper
= &gs_fontMapper
;
171 // ----------------------------------------------------------------------------
173 // ----------------------------------------------------------------------------
175 // change the config path during the lifetime of this object
176 class wxFontMapperPathChanger
179 wxFontMapperPathChanger(wxFontMapper
*fontMapper
, const wxString
& path
)
181 m_fontMapper
= fontMapper
;
182 m_ok
= m_fontMapper
->ChangePath(path
, &m_pathOld
);
185 bool IsOk() const { return m_ok
; }
187 ~wxFontMapperPathChanger()
190 m_fontMapper
->RestorePath(m_pathOld
);
194 wxFontMapper
*m_fontMapper
;
199 // ============================================================================
201 // ============================================================================
203 // ----------------------------------------------------------------------------
205 // ----------------------------------------------------------------------------
207 wxFontMapper::wxFontMapper()
211 #endif // wxUSE_CONFIG
214 m_windowParent
= NULL
;
218 wxFontMapper::~wxFontMapper()
222 // ----------------------------------------------------------------------------
224 // ----------------------------------------------------------------------------
228 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
230 return FONTMAPPER_ROOT_PATH
;
233 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
235 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
236 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
238 m_configRootPath
= prefix
;
241 // ----------------------------------------------------------------------------
242 // get config object and path for it
243 // ----------------------------------------------------------------------------
245 wxConfigBase
*wxFontMapper::GetConfig()
250 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
254 // we still want to have a config object because otherwise we would
255 // keep asking the user the same questions in the interactive mode,
256 // so create a dummy config which won't write to any files/registry
257 // but will allow us to remember the results of the questions at
258 // least during this run
259 m_config
= new wxMemoryConfig
;
260 wxConfig::Set(m_config
);
267 const wxString
& wxFontMapper::GetConfigPath()
269 if ( !m_configRootPath
)
272 m_configRootPath
= GetDefaultConfigPath();
275 return m_configRootPath
;
279 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
282 wxConfigBase
*config
= GetConfig();
286 *pathOld
= config
->GetPath();
288 wxString path
= GetConfigPath();
289 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
291 path
+= wxCONFIG_PATH_SEPARATOR
;
294 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
295 wxT("should be a relative path") );
299 config
->SetPath(path
);
307 void wxFontMapper::RestorePath(const wxString
& pathOld
)
310 GetConfig()->SetPath(pathOld
);
315 // ----------------------------------------------------------------------------
316 // charset/encoding correspondence
317 // ----------------------------------------------------------------------------
320 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
322 if ( encoding
== wxFONTENCODING_DEFAULT
)
324 return _("Default encoding");
327 size_t count
= WXSIZEOF(gs_encodingDescs
);
329 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
330 wxT("inconsitency detected - forgot to update one of the arrays?") );
332 for ( size_t i
= 0; i
< count
; i
++ )
334 if ( gs_encodings
[i
] == encoding
)
336 return wxGetTranslation(gs_encodingDescs
[i
]);
341 str
.Printf(_("Unknown encoding (%d)"), encoding
);
347 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
349 if ( encoding
== wxFONTENCODING_DEFAULT
)
354 size_t count
= WXSIZEOF(gs_encodingNames
);
356 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
357 wxT("inconsistency detected - forgot to update one of the arrays?") );
359 for ( size_t i
= 0; i
< count
; i
++ )
361 if ( gs_encodings
[i
] == encoding
)
363 return wxGetTranslation(gs_encodingNames
[i
]);
368 str
.Printf(_("unknown-%d"), encoding
);
373 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
376 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
378 // we're going to modify it, make a copy
379 wxString cs
= charset
;
382 // first try the user-defined settings
384 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
386 wxConfigBase
*config
= GetConfig();
388 // do we have an encoding for this charset?
389 long value
= config
->Read(charset
, -1l);
392 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
394 encoding
= (wxFontEncoding
)value
;
398 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
399 value
, charset
.c_str());
403 if ( encoding
== wxFONTENCODING_SYSTEM
)
405 // may be we have an alias?
406 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
408 wxString alias
= config
->Read(charset
);
411 // yes, we do - use it instead
416 RestorePath(pathOld
);
420 // if didn't find it there, try to recognize it ourselves
421 if ( encoding
== wxFONTENCODING_SYSTEM
)
427 // discard the optional quotes
430 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
432 cs
= wxString(cs
.c_str(), cs
.length() - 1);
438 if ( !cs
|| cs
== wxT("US-ASCII") )
440 encoding
= wxFONTENCODING_DEFAULT
;
442 else if ( cs
== wxT("UTF-7") )
444 encoding
= wxFONTENCODING_UTF7
;
446 else if ( cs
== wxT("UTF-8") )
448 encoding
= wxFONTENCODING_UTF8
;
450 else if ( cs
== wxT("KOI8-R") ||
451 cs
== wxT("KOI8-U") ||
452 cs
== wxT("KOI8-RU") )
454 // although koi8-ru is not strictly speaking the same as koi8-r,
455 // they are similar enough to make mapping it to koi8 better than
456 // not reckognizing it at all
457 encoding
= wxFONTENCODING_KOI8
;
459 else if ( cs
.Left(3) == wxT("ISO") )
461 // the dash is optional (or, to be exact, it is not, but
462 // several brokenmails "forget" it)
463 const wxChar
*p
= cs
.c_str() + 3;
464 if ( *p
== wxT('-') )
468 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
470 // make it 0 based and check that it is strictly positive in
471 // the process (no such thing as iso8859-0 encoding)
472 if ( (value
-- > 0) &&
473 (value
< wxFONTENCODING_ISO8859_MAX
-
474 wxFONTENCODING_ISO8859_1
) )
476 // it's a valid ISO8859 encoding
477 value
+= wxFONTENCODING_ISO8859_1
;
478 encoding
= (wxFontEncoding
)value
;
482 else // check for Windows charsets
485 if ( cs
.Left(7) == wxT("WINDOWS") )
489 else if ( cs
.Left(2) == wxT("CP") )
493 else // not a Windows encoding
500 const wxChar
*p
= cs
.c_str() + len
;
501 if ( *p
== wxT('-') )
505 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
510 if ( value
< wxFONTENCODING_CP12_MAX
-
511 wxFONTENCODING_CP1250
)
513 // a valid Windows code page
514 value
+= wxFONTENCODING_CP1250
;
515 encoding
= (wxFontEncoding
)value
;
525 // if still no luck, ask the user - unless disabled
526 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
528 // prepare the dialog data
531 wxString
title(m_titleDialog
);
533 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
537 msg
.Printf(_("The charset '%s' is unknown. You may select\nanother charset to replace it with or choose\n[Cancel] if it cannot be replaced"), charset
.c_str());
539 // the list of choices
540 size_t count
= WXSIZEOF(gs_encodingDescs
);
542 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
543 wxT("inconsitency detected - forgot to update one of the arrays?") );
545 wxString
*encodingNamesTranslated
= new wxString
[count
];
547 for ( size_t i
= 0; i
< count
; i
++ )
549 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
553 wxWindow
*parent
= m_windowParent
;
555 parent
= wxTheApp
->GetTopWindow();
557 // do ask the user and get back the index in encodings table
558 int n
= wxGetSingleChoiceIndex(msg
, title
,
560 encodingNamesTranslated
,
563 delete [] encodingNamesTranslated
;
567 encoding
= gs_encodings
[n
];
570 // save the result in the config now
571 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
573 wxConfigBase
*config
= GetConfig();
575 // remember the alt encoding for this charset
576 if ( !config
->Write(charset
, (long)encoding
) )
578 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
581 RestorePath(pathOld
);
583 #endif // wxUSE_CONFIG
592 // ----------------------------------------------------------------------------
593 // support for unknown encodings: we maintain a map between the
594 // (platform-specific) strings identifying them and our wxFontEncodings they
595 // correspond to which is used by GetFontForEncoding() function
596 // ----------------------------------------------------------------------------
600 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
601 wxFontEncoding encReplacement
,
602 wxNativeEncodingInfo
*info
)
604 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
605 wxTestFontEncoding(*info
) )
608 // remember the mapping in the config
609 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
613 GetConfig()->Write(configEntry
, info
->ToString());
615 #endif // wxUSE_CONFIG
623 class ReentrancyBlocker
626 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
627 ~ReentrancyBlocker() { m_b
= FALSE
; }
634 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
635 wxNativeEncodingInfo
*info
,
636 const wxString
& facename
,
640 // we need a flag to prevent infinite recursion which happens, for
641 // example, when GetAltForEncoding() is called from an OnPaint() handler:
642 // in this case, wxYield() which is called from wxMessageBox() we use here
643 // will lead to another call of OnPaint() and hence to another call of
644 // GetAltForEncoding() - and it is impossible to catch this from the user
645 // code because we are called from wxFont ctor implicitly.
647 // assume we're always called from the main thread, so that it is safe to
649 static bool s_inGetAltForEncoding
= FALSE
;
651 if ( interactive
&& s_inGetAltForEncoding
)
654 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
657 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
659 info
->facename
= facename
;
661 if ( encoding
== wxFONTENCODING_DEFAULT
)
663 encoding
= wxFont::GetDefaultEncoding();
666 // if we failed to load the system default encoding, something is really
667 // wrong and we'd better stop now - otherwise we will go into endless
668 // recursion trying to create the font in the msg box with the error
670 if ( encoding
== wxFONTENCODING_SYSTEM
)
672 wxFatalError(_("can't load any font, aborting"));
674 // wxFatalError doesn't return
677 wxString configEntry
, encName
= GetEncodingName(encoding
);
680 configEntry
= facename
+ _T("_");
682 configEntry
+= encName
;
685 // do we have a font spec for this encoding?
687 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
689 wxConfigBase
*config
= GetConfig();
691 wxString fontinfo
= config
->Read(configEntry
);
693 RestorePath(pathOld
);
695 if ( !!fontinfo
&& !!facename
)
697 // we tried to find a match with facename - now try without it
698 fontinfo
= config
->Read(encName
);
703 if ( info
->FromString(fontinfo
) )
705 if ( wxTestFontEncoding(*info
) )
710 //else: no such fonts, look for something else
714 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"), fontinfo
.c_str());
717 //else: there is no information in config about this encoding
719 #endif // wxUSE_CONFIG
724 wxString
title(m_titleDialog
);
726 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
730 msg
.Printf(_("The encoding '%s' is unknown.\nWould you like to select a font to be used for this encoding\n(otherwise the text in this encoding will not be shown correctly)?"),
731 GetEncodingDescription(encoding
).c_str());
733 wxWindow
*parent
= m_windowParent
;
735 parent
= wxTheApp
->GetTopWindow();
737 if ( wxMessageBox(msg
, title
,
738 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
741 data
.SetEncoding(encoding
);
742 data
.EncodingInfo() = *info
;
743 wxFontDialog
dialog(parent
, &data
);
744 if ( dialog
.ShowModal() == wxID_OK
)
746 wxFontData retData
= dialog
.GetFontData();
747 wxFont font
= retData
.GetChosenFont();
749 *info
= retData
.EncodingInfo();
750 info
-> encoding
= retData
.GetEncoding();
753 // remember this in the config
754 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
756 GetConfig()->Write(configEntry
, info
->ToString());
758 RestorePath(pathOld
);
760 #endif // wxUSE_CONFIG
764 //else: the user canceled the font selection dialog
766 //else: the user doesn't want to select a font
768 //else: we're in non-interactive mode
770 // now try the default mappings:
771 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
772 size_t count
= equiv
.GetCount();
775 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
777 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
785 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
786 wxFontEncoding
*alt_encoding
,
787 const wxString
& facename
,
790 wxNativeEncodingInfo info
;
791 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
792 *alt_encoding
= info
.encoding
;
796 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
797 const wxString
& facename
)
799 wxNativeEncodingInfo info
;
801 if (wxGetNativeFontEncoding(encoding
, &info
))
803 info
.facename
= facename
;
804 return wxTestFontEncoding(info
);