1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/font.cpp
3 // Purpose:     wxFont for wxGTK
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling and Julian Smart
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // ============================================================================
10 // declarations
11 // ============================================================================
12 
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16 
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19 
20 #include "wx/font.h"
21 
22 #ifndef WX_PRECOMP
23     #include "wx/intl.h"
24     #include "wx/log.h"
25     #include "wx/utils.h"
26     #include "wx/settings.h"
27     #include "wx/gdicmn.h"
28 #endif
29 
30 #include "wx/fontutil.h"
31 #include "wx/tokenzr.h"
32 
33 #include "wx/gtk/private.h"
34 
35 // ----------------------------------------------------------------------------
36 // wxFontRefData
37 // ----------------------------------------------------------------------------
38 
39 class wxFontRefData : public wxGDIRefData
40 {
41 public:
42     // main and also default ctor
43     wxFontRefData(const wxFontInfo& info = wxFontInfo());
44 
45     wxFontRefData(const wxString& nativeFontInfoString);
46 
47     // copy ctor
48     wxFontRefData( const wxFontRefData& data );
49 
50     // setters: all of them also take care to modify m_nativeFontInfo if we
51     // have it so as to not lose the information not carried by our fields
52     void SetFractionalPointSize(double pointSize);
53     void SetFamily(wxFontFamily family);
54     void SetStyle(wxFontStyle style);
55     void SetWeight(wxFontWeight weight);
56     void SetNumericWeight(int weight);
57     void SetUnderlined(bool underlined);
58     void SetStrikethrough(bool strikethrough);
59     bool SetFaceName(const wxString& facename);
60     void SetEncoding(wxFontEncoding encoding);
61 
62     // and this one also modifies all the other font data fields
63     void SetNativeFontInfo(const wxNativeFontInfo& info);
64 
65 protected:
66     // set all fields from (already initialized and valid) m_nativeFontInfo
67     void InitFromNative();
68 
69 private:
70     // The native font info: basically a PangoFontDescription, plus
71     // 'underlined' and 'strikethrough' attributes not supported by Pango.
72     wxNativeFontInfo m_nativeFontInfo;
73 
74     friend class wxFont;
75 };
76 
77 #define M_FONTDATA ((wxFontRefData*)m_refData)
78 
79 // ----------------------------------------------------------------------------
80 // wxFontRefData
81 // ----------------------------------------------------------------------------
82 
wxFontRefData(const wxFontInfo & info)83 wxFontRefData::wxFontRefData(const wxFontInfo& info)
84 {
85     // Create native font info
86     m_nativeFontInfo.description = pango_font_description_new();
87 
88     // And set its values
89     if ( info.HasFaceName() )
90     {
91         pango_font_description_set_family( m_nativeFontInfo.description,
92                                            wxGTK_CONV_SYS(info.GetFaceName()) );
93     }
94     else
95     {
96         wxFontFamily family = info.GetFamily();
97         if (family == wxFONTFAMILY_DEFAULT)
98             family = wxFONTFAMILY_SWISS;
99         SetFamily(family);
100     }
101 
102     SetStyle( info.GetStyle() );
103     m_nativeFontInfo.SetSizeOrDefault(info.GetFractionalPointSize());
104     SetNumericWeight( info.GetNumericWeight() );
105     SetUnderlined( info.IsUnderlined() );
106     SetStrikethrough( info.IsStrikethrough() );
107 }
108 
InitFromNative()109 void wxFontRefData::InitFromNative()
110 {
111     // Get native info
112     PangoFontDescription *desc = m_nativeFontInfo.description;
113 
114     // Pango sometimes needs to have a size
115     int pango_size = pango_font_description_get_size( desc );
116     if (pango_size == 0)
117         m_nativeFontInfo.SetSizeOrDefault(-1); // i.e. default
118 }
119 
wxFontRefData(const wxFontRefData & data)120 wxFontRefData::wxFontRefData( const wxFontRefData& data )
121              : wxGDIRefData(),
122                m_nativeFontInfo(data.m_nativeFontInfo)
123 {
124 }
125 
wxFontRefData(const wxString & nativeFontInfoString)126 wxFontRefData::wxFontRefData(const wxString& nativeFontInfoString)
127 {
128     m_nativeFontInfo.FromString( nativeFontInfoString );
129 
130     InitFromNative();
131 }
132 
133 // ----------------------------------------------------------------------------
134 // wxFontRefData SetXXX()
135 // ----------------------------------------------------------------------------
136 
SetFractionalPointSize(double pointSize)137 void wxFontRefData::SetFractionalPointSize(double pointSize)
138 {
139     m_nativeFontInfo.SetFractionalPointSize(pointSize);
140 }
141 
142 /*
143     NOTE: disabled because pango_font_description_set_absolute_size() and
144           wxDC::GetCharHeight() do not mix well: setting with the former a pixel
145           size of "30" makes the latter return 36...
146           Besides, we need to return GetPointSize() a point size value even if
147           SetPixelSize() was used and this would require further changes
148           (and use of pango_font_description_get_size_is_absolute in some places).
149 
150 bool wxFontRefData::SetPixelSize(const wxSize& pixelSize)
151 {
152     wxCHECK_MSG( pixelSize.GetWidth() >= 0 && pixelSize.GetHeight() > 0, false,
153                  "Negative values for the pixel size or zero pixel height are not allowed" );
154 
155     if (wx_pango_version_check(1,8,0) != NULL ||
156         pixelSize.GetWidth() != 0)
157     {
158         // NOTE: pango_font_description_set_absolute_size() only sets the font height;
159         //       if the user set the pixel width of the font explicitly or the pango
160         //       library is too old, we cannot proceed
161         return false;
162     }
163 
164     pango_font_description_set_absolute_size( m_nativeFontInfo.description,
165                                               pixelSize.GetHeight() * PANGO_SCALE );
166 
167     return true;
168 }
169 */
170 
SetFamily(wxFontFamily family)171 void wxFontRefData::SetFamily(wxFontFamily family)
172 {
173     m_nativeFontInfo.SetFamily(family);
174 }
175 
SetStyle(wxFontStyle style)176 void wxFontRefData::SetStyle(wxFontStyle style)
177 {
178     m_nativeFontInfo.SetStyle(style);
179 }
180 
SetWeight(wxFontWeight weight)181 void wxFontRefData::SetWeight(wxFontWeight weight)
182 {
183     m_nativeFontInfo.SetWeight(weight);
184 }
185 
SetNumericWeight(int weight)186 void wxFontRefData::SetNumericWeight(int weight)
187 {
188     m_nativeFontInfo.SetNumericWeight(weight);
189 }
190 
SetUnderlined(bool underlined)191 void wxFontRefData::SetUnderlined(bool underlined)
192 {
193     m_nativeFontInfo.SetUnderlined(underlined);
194 }
195 
SetStrikethrough(bool strikethrough)196 void wxFontRefData::SetStrikethrough(bool strikethrough)
197 {
198     m_nativeFontInfo.SetStrikethrough(strikethrough);
199 }
200 
SetFaceName(const wxString & facename)201 bool wxFontRefData::SetFaceName(const wxString& facename)
202 {
203     return m_nativeFontInfo.SetFaceName(facename);
204 }
205 
SetEncoding(wxFontEncoding WXUNUSED (encoding))206 void wxFontRefData::SetEncoding(wxFontEncoding WXUNUSED(encoding))
207 {
208     // with GTK+ 2 Pango always uses UTF8 internally, we cannot change it
209 }
210 
SetNativeFontInfo(const wxNativeFontInfo & info)211 void wxFontRefData::SetNativeFontInfo(const wxNativeFontInfo& info)
212 {
213     m_nativeFontInfo = info;
214 
215     // set all the other font parameters from the native font info
216     InitFromNative();
217 }
218 
219 // ----------------------------------------------------------------------------
220 // wxFont creation
221 // ----------------------------------------------------------------------------
222 
wxFont(const wxNativeFontInfo & info)223 wxFont::wxFont(const wxNativeFontInfo& info)
224 {
225     Create( info.GetPointSize(),
226             info.GetFamily(),
227             info.GetStyle(),
228             info.GetWeight(),
229             info.GetUnderlined(),
230             info.GetFaceName(),
231             info.GetEncoding() );
232 
233     if ( info.GetStrikethrough() )
234         SetStrikethrough(true);
235 }
236 
wxFont(const wxFontInfo & info)237 wxFont::wxFont(const wxFontInfo& info)
238 {
239     m_refData = new wxFontRefData(info);
240 
241     wxSize pixelSize = info.GetPixelSize();
242     if ( pixelSize != wxDefaultSize )
243         SetPixelSize(pixelSize);
244 }
245 
Create(int pointSize,wxFontFamily family,wxFontStyle style,wxFontWeight weight,bool underlined,const wxString & face,wxFontEncoding encoding)246 bool wxFont::Create( int pointSize,
247                      wxFontFamily family,
248                      wxFontStyle style,
249                      wxFontWeight weight,
250                      bool underlined,
251                      const wxString& face,
252                      wxFontEncoding encoding )
253 {
254     UnRef();
255 
256     m_refData = new wxFontRefData(InfoFromLegacyParams(pointSize, family,
257                                                        style, weight, underlined,
258                                                        face, encoding));
259 
260     return true;
261 }
262 
Create(const wxString & fontname)263 bool wxFont::Create(const wxString& fontname)
264 {
265     // VZ: does this really happen?
266     if ( fontname.empty() )
267     {
268         *this = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
269 
270         return true;
271     }
272 
273     m_refData = new wxFontRefData(fontname);
274 
275     return true;
276 }
277 
~wxFont()278 wxFont::~wxFont()
279 {
280 }
281 
282 // ----------------------------------------------------------------------------
283 // accessors
284 // ----------------------------------------------------------------------------
285 
GetFractionalPointSize() const286 double wxFont::GetFractionalPointSize() const
287 {
288     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
289 
290     return M_FONTDATA->m_nativeFontInfo.GetFractionalPointSize();
291 }
292 
GetFaceName() const293 wxString wxFont::GetFaceName() const
294 {
295     wxCHECK_MSG( IsOk(), wxEmptyString, wxT("invalid font") );
296 
297     return M_FONTDATA->m_nativeFontInfo.GetFaceName();
298 }
299 
DoGetFamily() const300 wxFontFamily wxFont::DoGetFamily() const
301 {
302     return M_FONTDATA->m_nativeFontInfo.GetFamily();
303 }
304 
GetStyle() const305 wxFontStyle wxFont::GetStyle() const
306 {
307     wxCHECK_MSG( IsOk(), wxFONTSTYLE_MAX, wxT("invalid font") );
308 
309     return M_FONTDATA->m_nativeFontInfo.GetStyle();
310 }
311 
GetNumericWeight() const312 int wxFont::GetNumericWeight() const
313 {
314     wxCHECK_MSG( IsOk(), wxFONTWEIGHT_MAX, "invalid font" );
315 
316     return M_FONTDATA->m_nativeFontInfo.GetNumericWeight();
317 }
318 
GetUnderlined() const319 bool wxFont::GetUnderlined() const
320 {
321     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
322 
323     return M_FONTDATA->m_nativeFontInfo.GetUnderlined();
324 }
325 
GetStrikethrough() const326 bool wxFont::GetStrikethrough() const
327 {
328     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
329 
330     return M_FONTDATA->m_nativeFontInfo.GetStrikethrough();
331 }
332 
GetEncoding() const333 wxFontEncoding wxFont::GetEncoding() const
334 {
335     wxCHECK_MSG( IsOk(), wxFONTENCODING_SYSTEM, wxT("invalid font") );
336 
337     return wxFONTENCODING_UTF8;
338         // Pango always uses UTF8... see also SetEncoding()
339 }
340 
GetNativeFontInfo() const341 const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
342 {
343     wxCHECK_MSG( IsOk(), NULL, wxT("invalid font") );
344 
345     return &(M_FONTDATA->m_nativeFontInfo);
346 }
347 
IsFixedWidth() const348 bool wxFont::IsFixedWidth() const
349 {
350     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
351 
352     return wxFontBase::IsFixedWidth();
353 }
354 
355 // ----------------------------------------------------------------------------
356 // change font attributes
357 // ----------------------------------------------------------------------------
358 
SetFractionalPointSize(double pointSize)359 void wxFont::SetFractionalPointSize(double pointSize)
360 {
361     AllocExclusive();
362 
363     M_FONTDATA->SetFractionalPointSize(pointSize);
364 }
365 
SetFamily(wxFontFamily family)366 void wxFont::SetFamily(wxFontFamily family)
367 {
368     AllocExclusive();
369 
370     M_FONTDATA->SetFamily(family);
371 }
372 
SetStyle(wxFontStyle style)373 void wxFont::SetStyle(wxFontStyle style)
374 {
375     AllocExclusive();
376 
377     M_FONTDATA->SetStyle(style);
378 }
379 
SetNumericWeight(int weight)380 void wxFont::SetNumericWeight(int weight)
381 {
382     AllocExclusive();
383 
384     M_FONTDATA->SetNumericWeight(weight);
385 }
386 
SetFaceName(const wxString & faceName)387 bool wxFont::SetFaceName(const wxString& faceName)
388 {
389     AllocExclusive();
390 
391     return M_FONTDATA->SetFaceName(faceName) &&
392            wxFontBase::SetFaceName(faceName);
393 }
394 
SetUnderlined(bool underlined)395 void wxFont::SetUnderlined(bool underlined)
396 {
397     AllocExclusive();
398 
399     M_FONTDATA->SetUnderlined(underlined);
400 }
401 
SetStrikethrough(bool strikethrough)402 void wxFont::SetStrikethrough(bool strikethrough)
403 {
404     AllocExclusive();
405 
406     M_FONTDATA->SetStrikethrough(strikethrough);
407 }
408 
SetEncoding(wxFontEncoding encoding)409 void wxFont::SetEncoding(wxFontEncoding encoding)
410 {
411     AllocExclusive();
412 
413     M_FONTDATA->SetEncoding(encoding);
414 }
415 
DoSetNativeFontInfo(const wxNativeFontInfo & info)416 void wxFont::DoSetNativeFontInfo( const wxNativeFontInfo& info )
417 {
418     AllocExclusive();
419 
420     M_FONTDATA->SetNativeFontInfo( info );
421 }
422 
CreateGDIRefData() const423 wxGDIRefData* wxFont::CreateGDIRefData() const
424 {
425     return new wxFontRefData;
426 }
427 
CloneGDIRefData(const wxGDIRefData * data) const428 wxGDIRefData* wxFont::CloneGDIRefData(const wxGDIRefData* data) const
429 {
430     return new wxFontRefData(*static_cast<const wxFontRefData*>(data));
431 }
432 
GTKSetPangoAttrs(PangoLayout * layout) const433 bool wxFont::GTKSetPangoAttrs(PangoLayout* layout) const
434 {
435     if (!IsOk() || !(GetUnderlined() || GetStrikethrough()))
436         return false;
437 
438     PangoAttrList* attrs = pango_attr_list_new();
439     PangoAttribute* a;
440 
441 #ifndef __WXGTK3__
442     if (wx_pango_version_check(1,16,0))
443     {
444         // a PangoLayout which has leading/trailing spaces with underlined font
445         // is not correctly drawn by this pango version: Pango won't underline the spaces.
446         // This can be a problem; e.g. wxHTML rendering of underlined text relies on
447         // this behaviour. To workaround this problem, we use a special hack here
448         // suggested by pango maintainer Behdad Esfahbod: we prepend and append two
449         // empty space characters and give them a dummy colour attribute.
450         // This will force Pango to underline the leading/trailing spaces, too.
451 
452         const char* text = pango_layout_get_text(layout);
453         const size_t n = strlen(text);
454         if ((n > 0 && text[0] == ' ') || (n > 1 && text[n - 1] == ' '))
455         {
456             wxCharBuffer buf(n + 6);
457             // copy the leading U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
458             memcpy(buf.data(), "\342\200\214", 3);
459             // copy the user string
460             memcpy(buf.data() + 3, text, n);
461             // copy the trailing U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
462             memcpy(buf.data() + 3 + n, "\342\200\214", 3);
463 
464             pango_layout_set_text(layout, buf, n + 6);
465 
466             // Add dummy attributes (use colour as it's invisible anyhow for 0
467             // width spaces) to ensure that the spaces in the beginning/end of the
468             // string are underlined too.
469             a = pango_attr_foreground_new(0x0057, 0x52A9, 0xD614);
470             a->start_index = 0;
471             a->end_index = 3;
472             pango_attr_list_insert(attrs, a);
473 
474             a = pango_attr_foreground_new(0x0057, 0x52A9, 0xD614);
475             a->start_index = n + 3;
476             a->end_index = n + 6;
477             pango_attr_list_insert(attrs, a);
478         }
479     }
480 #endif // !__WXGTK3__
481 
482     if (GetUnderlined())
483     {
484         a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
485         pango_attr_list_insert(attrs, a);
486     }
487     if (GetStrikethrough())
488     {
489         a = pango_attr_strikethrough_new(true);
490         pango_attr_list_insert(attrs, a);
491     }
492 
493     pango_layout_set_attributes(layout, attrs);
494     pango_attr_list_unref(attrs);
495 
496     return true;
497 }
498 
499 
500 // ----------------------------------------------------------------------------
501 // Support for adding private fonts
502 // ----------------------------------------------------------------------------
503 
504 #if wxUSE_PRIVATE_FONTS
505 
506 #include "wx/fontenum.h"
507 #include "wx/module.h"
508 #include "wx/gtk/private.h"
509 #include "wx/gtk/private/object.h"
510 
511 #include <fontconfig/fontconfig.h>
512 #include <pango/pangofc-fontmap.h>
513 
514 extern PangoContext* wxGetPangoContext();
515 
516 namespace
517 {
518 
519 FcConfig* gs_fcConfig = NULL;
520 
521 // Module used to clean up the global FcConfig.
522 class wxFcConfigDestroyModule : public wxModule
523 {
524 public:
wxFcConfigDestroyModule()525     wxFcConfigDestroyModule() { }
526 
OnInit()527     bool OnInit() wxOVERRIDE { return true; }
OnExit()528     void OnExit() wxOVERRIDE
529     {
530         if ( gs_fcConfig )
531         {
532             FcConfigDestroy(gs_fcConfig);
533             gs_fcConfig = NULL;
534         }
535     }
536 
537 private:
538     wxDECLARE_DYNAMIC_CLASS(wxFcConfigDestroyModule);
539 };
540 
541 wxIMPLEMENT_DYNAMIC_CLASS(wxFcConfigDestroyModule, wxModule);
542 
543 } // anonymous namespace
544 
AddPrivateFont(const wxString & filename)545 bool wxFontBase::AddPrivateFont(const wxString& filename)
546 {
547     // We already checked that we have the required functions at compile-time,
548     // but we should also check if they're available at run-time in case we use
549     // older versions of them than the ones we were compiled with.
550     if ( wx_pango_version_check(1,38,0) != NULL )
551     {
552         wxLogError(_("Using private fonts is not supported on this system: "
553                      "Pango library is too old, 1.38 or later required."));
554         return false;
555     }
556 
557     if ( !gs_fcConfig )
558     {
559         gs_fcConfig = FcInitLoadConfigAndFonts();
560         if ( !gs_fcConfig )
561         {
562             wxLogError(_("Failed to create font configuration object."));
563             return false;
564         }
565     }
566 
567     if ( !FcConfigAppFontAddFile(gs_fcConfig,
568             reinterpret_cast<const FcChar8*>(
569                 static_cast<const char*>(filename.utf8_str())
570             )) )
571     {
572         wxLogError(_("Failed to add custom font \"%s\"."), filename);
573         return false;
574     }
575 
576     wxGtkObject<PangoContext> context(wxGetPangoContext());
577     PangoFontMap* const fmap = pango_context_get_font_map(context);
578     if ( !fmap || !PANGO_IS_FC_FONT_MAP(fmap) )
579     {
580         wxLogError(_("Failed to register font configuration using private fonts."));
581         return false;
582     }
583 
584     PangoFcFontMap* const fcfmap = PANGO_FC_FONT_MAP(fmap);
585     pango_fc_font_map_set_config(fcfmap, gs_fcConfig);
586 
587     // Ensure that the face names defined by private fonts are recognized by
588     // our SetFaceName() which uses wxFontEnumerator to check if the name is in
589     // the list of available faces.
590     wxFontEnumerator::InvalidateCache();
591 
592     return true;
593 }
594 
595 #endif // wxUSE_PRIVATE_FONTS
596 
597