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/log.h"
24     #include "wx/utils.h"
25     #include "wx/settings.h"
26     #include "wx/gdicmn.h"
27 #endif
28 
29 #include "wx/fontutil.h"
30 #include "wx/tokenzr.h"
31 
32 #include "wx/gtk/private.h"
33 
34 // ----------------------------------------------------------------------------
35 // constants
36 // ----------------------------------------------------------------------------
37 
38 // the default size (in points) for the fonts
39 static const int wxDEFAULT_FONT_SIZE = 12;
40 
41 // ----------------------------------------------------------------------------
42 // wxFontRefData
43 // ----------------------------------------------------------------------------
44 
45 class wxFontRefData : public wxGDIRefData
46 {
47 public:
48     // from broken down font parameters, also default ctor
49     wxFontRefData(int size = -1,
50                   wxFontFamily family = wxFONTFAMILY_DEFAULT,
51                   wxFontStyle style = wxFONTSTYLE_NORMAL,
52                   wxFontWeight weight = wxFONTWEIGHT_NORMAL,
53                   bool underlined = false,
54                   bool strikethrough = false,
55                   const wxString& faceName = wxEmptyString,
56                   wxFontEncoding encoding = wxFONTENCODING_DEFAULT);
57 
58     wxFontRefData(const wxString& nativeFontInfoString);
59 
60     // copy ctor
61     wxFontRefData( const wxFontRefData& data );
62 
63     virtual ~wxFontRefData();
64 
65     // setters: all of them also take care to modify m_nativeFontInfo if we
66     // have it so as to not lose the information not carried by our fields
67     void SetPointSize(int pointSize);
68     void SetFamily(wxFontFamily family);
69     void SetStyle(wxFontStyle style);
70     void SetWeight(wxFontWeight weight);
71     void SetUnderlined(bool underlined);
72     void SetStrikethrough(bool strikethrough);
73     bool SetFaceName(const wxString& facename);
74     void SetEncoding(wxFontEncoding encoding);
75 
76     // and this one also modifies all the other font data fields
77     void SetNativeFontInfo(const wxNativeFontInfo& info);
78 
79 protected:
80     // common part of all ctors
81     void Init(int pointSize,
82               wxFontFamily family,
83               wxFontStyle style,
84               wxFontWeight weight,
85               bool underlined,
86               bool strikethrough,
87               const wxString& faceName,
88               wxFontEncoding encoding);
89 
90     // set all fields from (already initialized and valid) m_nativeFontInfo
91     void InitFromNative();
92 
93 private:
94     // The native font info: basically a PangoFontDescription, plus
95     // 'underlined' and 'strikethrough' attributes not supported by Pango.
96     wxNativeFontInfo m_nativeFontInfo;
97 
98     friend class wxFont;
99 };
100 
101 #define M_FONTDATA ((wxFontRefData*)m_refData)
102 
103 // ----------------------------------------------------------------------------
104 // wxFontRefData
105 // ----------------------------------------------------------------------------
106 
Init(int pointSize,wxFontFamily family,wxFontStyle style,wxFontWeight weight,bool underlined,bool strikethrough,const wxString & faceName,wxFontEncoding WXUNUSED (encoding))107 void wxFontRefData::Init(int pointSize,
108                          wxFontFamily family,
109                          wxFontStyle style,
110                          wxFontWeight weight,
111                          bool underlined,
112                          bool strikethrough,
113                          const wxString& faceName,
114                          wxFontEncoding WXUNUSED(encoding))
115 {
116     if (family == wxFONTFAMILY_DEFAULT)
117         family = wxFONTFAMILY_SWISS;
118 
119     // Create native font info
120     m_nativeFontInfo.description = pango_font_description_new();
121 
122     // And set its values
123     if (!faceName.empty())
124     {
125         pango_font_description_set_family( m_nativeFontInfo.description,
126                                            wxGTK_CONV_SYS(faceName) );
127     }
128     else
129     {
130         SetFamily(family);
131     }
132 
133     SetStyle( style == wxDEFAULT ? wxFONTSTYLE_NORMAL : style );
134     SetPointSize( (pointSize == wxDEFAULT || pointSize == -1)
135                     ? wxDEFAULT_FONT_SIZE
136                     : pointSize );
137     SetWeight( weight == wxDEFAULT ? wxFONTWEIGHT_NORMAL : weight );
138     SetUnderlined( underlined );
139     SetStrikethrough( strikethrough );
140 }
141 
InitFromNative()142 void wxFontRefData::InitFromNative()
143 {
144     // Get native info
145     PangoFontDescription *desc = m_nativeFontInfo.description;
146 
147     // Pango sometimes needs to have a size
148     int pango_size = pango_font_description_get_size( desc );
149     if (pango_size == 0)
150         m_nativeFontInfo.SetPointSize(wxDEFAULT_FONT_SIZE);
151 }
152 
wxFontRefData(const wxFontRefData & data)153 wxFontRefData::wxFontRefData( const wxFontRefData& data )
154              : wxGDIRefData(),
155                m_nativeFontInfo(data.m_nativeFontInfo)
156 {
157 }
158 
wxFontRefData(int size,wxFontFamily family,wxFontStyle style,wxFontWeight weight,bool underlined,bool strikethrough,const wxString & faceName,wxFontEncoding encoding)159 wxFontRefData::wxFontRefData(int size, wxFontFamily family, wxFontStyle style,
160                              wxFontWeight weight, bool underlined, bool strikethrough,
161                              const wxString& faceName,
162                              wxFontEncoding encoding)
163 {
164     Init(size, family, style, weight, underlined, strikethrough, faceName, encoding);
165 }
166 
wxFontRefData(const wxString & nativeFontInfoString)167 wxFontRefData::wxFontRefData(const wxString& nativeFontInfoString)
168 {
169     m_nativeFontInfo.FromString( nativeFontInfoString );
170 
171     InitFromNative();
172 }
173 
~wxFontRefData()174 wxFontRefData::~wxFontRefData()
175 {
176 }
177 
178 // ----------------------------------------------------------------------------
179 // wxFontRefData SetXXX()
180 // ----------------------------------------------------------------------------
181 
SetPointSize(int pointSize)182 void wxFontRefData::SetPointSize(int pointSize)
183 {
184     m_nativeFontInfo.SetPointSize(pointSize);
185 }
186 
187 /*
188     NOTE: disabled because pango_font_description_set_absolute_size() and
189           wxDC::GetCharHeight() do not mix well: setting with the former a pixel
190           size of "30" makes the latter return 36...
191           Besides, we need to return GetPointSize() a point size value even if
192           SetPixelSize() was used and this would require further changes
193           (and use of pango_font_description_get_size_is_absolute in some places).
194 
195 bool wxFontRefData::SetPixelSize(const wxSize& pixelSize)
196 {
197     wxCHECK_MSG( pixelSize.GetWidth() >= 0 && pixelSize.GetHeight() > 0, false,
198                  "Negative values for the pixel size or zero pixel height are not allowed" );
199 
200     if (wx_pango_version_check(1,8,0) != NULL ||
201         pixelSize.GetWidth() != 0)
202     {
203         // NOTE: pango_font_description_set_absolute_size() only sets the font height;
204         //       if the user set the pixel width of the font explicitly or the pango
205         //       library is too old, we cannot proceed
206         return false;
207     }
208 
209     pango_font_description_set_absolute_size( m_nativeFontInfo.description,
210                                               pixelSize.GetHeight() * PANGO_SCALE );
211 
212     return true;
213 }
214 */
215 
SetFamily(wxFontFamily family)216 void wxFontRefData::SetFamily(wxFontFamily family)
217 {
218     m_nativeFontInfo.SetFamily(family);
219 }
220 
SetStyle(wxFontStyle style)221 void wxFontRefData::SetStyle(wxFontStyle style)
222 {
223     m_nativeFontInfo.SetStyle(style);
224 }
225 
SetWeight(wxFontWeight weight)226 void wxFontRefData::SetWeight(wxFontWeight weight)
227 {
228     m_nativeFontInfo.SetWeight(weight);
229 }
230 
SetUnderlined(bool underlined)231 void wxFontRefData::SetUnderlined(bool underlined)
232 {
233     m_nativeFontInfo.SetUnderlined(underlined);
234 }
235 
SetStrikethrough(bool strikethrough)236 void wxFontRefData::SetStrikethrough(bool strikethrough)
237 {
238     m_nativeFontInfo.SetStrikethrough(strikethrough);
239 }
240 
SetFaceName(const wxString & facename)241 bool wxFontRefData::SetFaceName(const wxString& facename)
242 {
243     return m_nativeFontInfo.SetFaceName(facename);
244 }
245 
SetEncoding(wxFontEncoding WXUNUSED (encoding))246 void wxFontRefData::SetEncoding(wxFontEncoding WXUNUSED(encoding))
247 {
248     // with GTK+ 2 Pango always uses UTF8 internally, we cannot change it
249 }
250 
SetNativeFontInfo(const wxNativeFontInfo & info)251 void wxFontRefData::SetNativeFontInfo(const wxNativeFontInfo& info)
252 {
253     m_nativeFontInfo = info;
254 
255     // set all the other font parameters from the native font info
256     InitFromNative();
257 }
258 
259 // ----------------------------------------------------------------------------
260 // wxFont creation
261 // ----------------------------------------------------------------------------
262 
wxFont(const wxNativeFontInfo & info)263 wxFont::wxFont(const wxNativeFontInfo& info)
264 {
265     Create( info.GetPointSize(),
266             info.GetFamily(),
267             info.GetStyle(),
268             info.GetWeight(),
269             info.GetUnderlined(),
270             info.GetFaceName(),
271             info.GetEncoding() );
272 
273     if ( info.GetStrikethrough() )
274         SetStrikethrough(true);
275 }
276 
wxFont(const wxFontInfo & info)277 wxFont::wxFont(const wxFontInfo& info)
278 {
279     m_refData = new wxFontRefData(info.GetPointSize(),
280                                   info.GetFamily(),
281                                   info.GetStyle(),
282                                   info.GetWeight(),
283                                   info.IsUnderlined(),
284                                   info.IsStrikethrough(),
285                                   info.GetFaceName(),
286                                   info.GetEncoding());
287 
288     wxSize pixelSize = info.GetPixelSize();
289     if ( pixelSize != wxDefaultSize )
290         SetPixelSize(pixelSize);
291 }
292 
Create(int pointSize,wxFontFamily family,wxFontStyle style,wxFontWeight weight,bool underlined,const wxString & face,wxFontEncoding encoding)293 bool wxFont::Create( int pointSize,
294                      wxFontFamily family,
295                      wxFontStyle style,
296                      wxFontWeight weight,
297                      bool underlined,
298                      const wxString& face,
299                      wxFontEncoding encoding )
300 {
301     UnRef();
302 
303     m_refData = new wxFontRefData(pointSize, family, style, weight,
304                                   underlined, false, face, encoding);
305 
306     return true;
307 }
308 
Create(const wxString & fontname)309 bool wxFont::Create(const wxString& fontname)
310 {
311     // VZ: does this really happen?
312     if ( fontname.empty() )
313     {
314         *this = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
315 
316         return true;
317     }
318 
319     m_refData = new wxFontRefData(fontname);
320 
321     return true;
322 }
323 
~wxFont()324 wxFont::~wxFont()
325 {
326 }
327 
328 // ----------------------------------------------------------------------------
329 // accessors
330 // ----------------------------------------------------------------------------
331 
GetPointSize() const332 int wxFont::GetPointSize() const
333 {
334     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
335 
336     return M_FONTDATA->m_nativeFontInfo.GetPointSize();
337 }
338 
GetFaceName() const339 wxString wxFont::GetFaceName() const
340 {
341     wxCHECK_MSG( IsOk(), wxEmptyString, wxT("invalid font") );
342 
343     return M_FONTDATA->m_nativeFontInfo.GetFaceName();
344 }
345 
DoGetFamily() const346 wxFontFamily wxFont::DoGetFamily() const
347 {
348     return M_FONTDATA->m_nativeFontInfo.GetFamily();
349 }
350 
GetStyle() const351 wxFontStyle wxFont::GetStyle() const
352 {
353     wxCHECK_MSG( IsOk(), wxFONTSTYLE_MAX, wxT("invalid font") );
354 
355     return M_FONTDATA->m_nativeFontInfo.GetStyle();
356 }
357 
GetWeight() const358 wxFontWeight wxFont::GetWeight() const
359 {
360     wxCHECK_MSG( IsOk(), wxFONTWEIGHT_MAX, wxT("invalid font") );
361 
362     return M_FONTDATA->m_nativeFontInfo.GetWeight();
363 }
364 
GetUnderlined() const365 bool wxFont::GetUnderlined() const
366 {
367     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
368 
369     return M_FONTDATA->m_nativeFontInfo.GetUnderlined();
370 }
371 
GetStrikethrough() const372 bool wxFont::GetStrikethrough() const
373 {
374     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
375 
376     return M_FONTDATA->m_nativeFontInfo.GetStrikethrough();
377 }
378 
GetEncoding() const379 wxFontEncoding wxFont::GetEncoding() const
380 {
381     wxCHECK_MSG( IsOk(), wxFONTENCODING_SYSTEM, wxT("invalid font") );
382 
383     return wxFONTENCODING_UTF8;
384         // Pango always uses UTF8... see also SetEncoding()
385 }
386 
GetNativeFontInfo() const387 const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
388 {
389     wxCHECK_MSG( IsOk(), NULL, wxT("invalid font") );
390 
391     return &(M_FONTDATA->m_nativeFontInfo);
392 }
393 
IsFixedWidth() const394 bool wxFont::IsFixedWidth() const
395 {
396     wxCHECK_MSG( IsOk(), false, wxT("invalid font") );
397 
398     return wxFontBase::IsFixedWidth();
399 }
400 
401 // ----------------------------------------------------------------------------
402 // change font attributes
403 // ----------------------------------------------------------------------------
404 
SetPointSize(int pointSize)405 void wxFont::SetPointSize(int pointSize)
406 {
407     AllocExclusive();
408 
409     M_FONTDATA->SetPointSize(pointSize);
410 }
411 
SetFamily(wxFontFamily family)412 void wxFont::SetFamily(wxFontFamily family)
413 {
414     AllocExclusive();
415 
416     M_FONTDATA->SetFamily(family);
417 }
418 
SetStyle(wxFontStyle style)419 void wxFont::SetStyle(wxFontStyle style)
420 {
421     AllocExclusive();
422 
423     M_FONTDATA->SetStyle(style);
424 }
425 
SetWeight(wxFontWeight weight)426 void wxFont::SetWeight(wxFontWeight weight)
427 {
428     AllocExclusive();
429 
430     M_FONTDATA->SetWeight(weight);
431 }
432 
SetFaceName(const wxString & faceName)433 bool wxFont::SetFaceName(const wxString& faceName)
434 {
435     AllocExclusive();
436 
437     return M_FONTDATA->SetFaceName(faceName) &&
438            wxFontBase::SetFaceName(faceName);
439 }
440 
SetUnderlined(bool underlined)441 void wxFont::SetUnderlined(bool underlined)
442 {
443     AllocExclusive();
444 
445     M_FONTDATA->SetUnderlined(underlined);
446 }
447 
SetStrikethrough(bool strikethrough)448 void wxFont::SetStrikethrough(bool strikethrough)
449 {
450     AllocExclusive();
451 
452     M_FONTDATA->SetStrikethrough(strikethrough);
453 }
454 
SetEncoding(wxFontEncoding encoding)455 void wxFont::SetEncoding(wxFontEncoding encoding)
456 {
457     AllocExclusive();
458 
459     M_FONTDATA->SetEncoding(encoding);
460 }
461 
DoSetNativeFontInfo(const wxNativeFontInfo & info)462 void wxFont::DoSetNativeFontInfo( const wxNativeFontInfo& info )
463 {
464     AllocExclusive();
465 
466     M_FONTDATA->SetNativeFontInfo( info );
467 }
468 
CreateGDIRefData() const469 wxGDIRefData* wxFont::CreateGDIRefData() const
470 {
471     return new wxFontRefData;
472 }
473 
CloneGDIRefData(const wxGDIRefData * data) const474 wxGDIRefData* wxFont::CloneGDIRefData(const wxGDIRefData* data) const
475 {
476     return new wxFontRefData(*static_cast<const wxFontRefData*>(data));
477 }
478 
GTKSetPangoAttrs(PangoLayout * layout) const479 bool wxFont::GTKSetPangoAttrs(PangoLayout* layout) const
480 {
481     if (!IsOk() || !(GetUnderlined() || GetStrikethrough()))
482         return false;
483 
484     PangoAttrList* attrs = pango_attr_list_new();
485     PangoAttribute* a;
486 
487 #ifndef __WXGTK3__
488     if (wx_pango_version_check(1,16,0))
489     {
490         // a PangoLayout which has leading/trailing spaces with underlined font
491         // is not correctly drawn by this pango version: Pango won't underline the spaces.
492         // This can be a problem; e.g. wxHTML rendering of underlined text relies on
493         // this behaviour. To workaround this problem, we use a special hack here
494         // suggested by pango maintainer Behdad Esfahbod: we prepend and append two
495         // empty space characters and give them a dummy colour attribute.
496         // This will force Pango to underline the leading/trailing spaces, too.
497 
498         const char* text = pango_layout_get_text(layout);
499         const size_t n = strlen(text);
500         if ((n > 0 && text[0] == ' ') || (n > 1 && text[n - 1] == ' '))
501         {
502             wxCharBuffer buf(n + 6);
503             // copy the leading U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
504             memcpy(buf.data(), "\342\200\214", 3);
505             // copy the user string
506             memcpy(buf.data() + 3, text, n);
507             // copy the trailing U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
508             memcpy(buf.data() + 3 + n, "\342\200\214", 3);
509 
510             pango_layout_set_text(layout, buf, n + 6);
511 
512             // Add dummy attributes (use colour as it's invisible anyhow for 0
513             // width spaces) to ensure that the spaces in the beginning/end of the
514             // string are underlined too.
515             a = pango_attr_foreground_new(0x0057, 0x52A9, 0xD614);
516             a->start_index = 0;
517             a->end_index = 3;
518             pango_attr_list_insert(attrs, a);
519 
520             a = pango_attr_foreground_new(0x0057, 0x52A9, 0xD614);
521             a->start_index = n + 3;
522             a->end_index = n + 6;
523             pango_attr_list_insert(attrs, a);
524         }
525     }
526 #endif // !__WXGTK3__
527 
528     if (GetUnderlined())
529     {
530         a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
531         pango_attr_list_insert(attrs, a);
532     }
533     if (GetStrikethrough())
534     {
535         a = pango_attr_strikethrough_new(true);
536         pango_attr_list_insert(attrs, a);
537     }
538 
539     pango_layout_set_attributes(layout, attrs);
540     pango_attr_list_unref(attrs);
541 
542     return true;
543 }
544