• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/gfx/platform_font_pango.h"
6 
7 #include <fontconfig/fontconfig.h>
8 #include <pango/pango.h>
9 
10 #include <algorithm>
11 #include <string>
12 
13 #include "base/logging.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "third_party/skia/include/core/SkPaint.h"
18 #include "third_party/skia/include/core/SkString.h"
19 #include "third_party/skia/include/core/SkTypeface.h"
20 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/font.h"
22 #include "ui/gfx/pango_util.h"
23 
24 #if defined(TOOLKIT_GTK)
25 #include <gdk/gdk.h>
26 #include <gtk/gtk.h>
27 #endif
28 
29 namespace {
30 
31 // The font family name which is used when a user's application font for
32 // GNOME/KDE is a non-scalable one. The name should be listed in the
33 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
34 const char* kFallbackFontFamilyName = "sans";
35 
36 // Returns the available font family that best (in FontConfig's eyes) matches
37 // the supplied list of family names.
FindBestMatchFontFamilyName(const std::vector<std::string> & family_names)38 std::string FindBestMatchFontFamilyName(
39     const std::vector<std::string>& family_names) {
40   FcPattern* pattern = FcPatternCreate();
41   for (std::vector<std::string>::const_iterator it = family_names.begin();
42        it != family_names.end(); ++it) {
43     FcValue fcvalue;
44     fcvalue.type = FcTypeString;
45     fcvalue.u.s = reinterpret_cast<const FcChar8*>(it->c_str());
46     FcPatternAdd(pattern, FC_FAMILY, fcvalue, FcTrue /* append */);
47   }
48 
49   FcConfigSubstitute(0, pattern, FcMatchPattern);
50   FcDefaultSubstitute(pattern);
51   FcResult result;
52   FcPattern* match = FcFontMatch(0, pattern, &result);
53   DCHECK(match) << "Could not find font";
54   FcChar8* match_family = NULL;
55   FcPatternGetString(match, FC_FAMILY, 0, &match_family);
56   std::string font_family(reinterpret_cast<char*>(match_family));
57   FcPatternDestroy(pattern);
58   FcPatternDestroy(match);
59   return font_family;
60 }
61 
62 }  // namespace
63 
64 namespace gfx {
65 
66 // static
67 Font* PlatformFontPango::default_font_ = NULL;
68 
69 #if defined(OS_CHROMEOS)
70 // static
71 std::string* PlatformFontPango::default_font_description_ = NULL;
72 #endif
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 // PlatformFontPango, public:
76 
PlatformFontPango()77 PlatformFontPango::PlatformFontPango() {
78   if (default_font_ == NULL) {
79     std::string font_name = GetDefaultFont();
80 
81     ScopedPangoFontDescription desc(
82         pango_font_description_from_string(font_name.c_str()));
83     default_font_ = new Font(desc.get());
84 
85     DCHECK(default_font_);
86   }
87 
88   InitFromPlatformFont(
89       static_cast<PlatformFontPango*>(default_font_->platform_font()));
90 }
91 
PlatformFontPango(NativeFont native_font)92 PlatformFontPango::PlatformFontPango(NativeFont native_font) {
93   std::vector<std::string> family_names;
94   base::SplitString(pango_font_description_get_family(native_font), ',',
95                     &family_names);
96   std::string font_family = FindBestMatchFontFamilyName(family_names);
97   InitWithNameAndSize(font_family, gfx::GetPangoFontSizeInPixels(native_font));
98 
99   int style = 0;
100   if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) {
101     // TODO(davemoore) What should we do about other weights? We currently
102     // only support BOLD.
103     style |= gfx::Font::BOLD;
104   }
105   if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) {
106     // TODO(davemoore) What about PANGO_STYLE_OBLIQUE?
107     style |= gfx::Font::ITALIC;
108   }
109   if (style != 0)
110     style_ = style;
111 }
112 
PlatformFontPango(const std::string & font_name,int font_size)113 PlatformFontPango::PlatformFontPango(const std::string& font_name,
114                                      int font_size) {
115   InitWithNameAndSize(font_name, font_size);
116 }
117 
underline_position() const118 double PlatformFontPango::underline_position() const {
119   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
120   return underline_position_pixels_;
121 }
122 
underline_thickness() const123 double PlatformFontPango::underline_thickness() const {
124   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
125   return underline_thickness_pixels_;
126 }
127 
128 ////////////////////////////////////////////////////////////////////////////////
129 // PlatformFontPango, PlatformFont implementation:
130 
131 // static
ReloadDefaultFont()132 void PlatformFontPango::ReloadDefaultFont() {
133   delete default_font_;
134   default_font_ = NULL;
135 }
136 
137 #if defined(OS_CHROMEOS)
138 // static
SetDefaultFontDescription(const std::string & font_description)139 void PlatformFontPango::SetDefaultFontDescription(
140     const std::string& font_description) {
141   delete default_font_description_;
142   default_font_description_ = new std::string(font_description);
143 }
144 
145 #endif
146 
DeriveFont(int size_delta,int style) const147 Font PlatformFontPango::DeriveFont(int size_delta, int style) const {
148   // If the delta is negative, if must not push the size below 1
149   if (size_delta < 0)
150     DCHECK_LT(-size_delta, font_size_pixels_);
151 
152   if (style == style_) {
153     // Fast path, we just use the same typeface at a different size
154     return Font(new PlatformFontPango(typeface_,
155                                       font_family_,
156                                       font_size_pixels_ + size_delta,
157                                       style_));
158   }
159 
160   // If the style has changed we may need to load a new face
161   int skstyle = SkTypeface::kNormal;
162   if (gfx::Font::BOLD & style)
163     skstyle |= SkTypeface::kBold;
164   if (gfx::Font::ITALIC & style)
165     skstyle |= SkTypeface::kItalic;
166 
167   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
168       SkTypeface::CreateFromName(
169           font_family_.c_str(),
170           static_cast<SkTypeface::Style>(skstyle)));
171 
172   return Font(new PlatformFontPango(typeface,
173                                     font_family_,
174                                     font_size_pixels_ + size_delta,
175                                     style));
176 }
177 
GetHeight() const178 int PlatformFontPango::GetHeight() const {
179   return height_pixels_;
180 }
181 
GetBaseline() const182 int PlatformFontPango::GetBaseline() const {
183   return ascent_pixels_;
184 }
185 
GetCapHeight() const186 int PlatformFontPango::GetCapHeight() const {
187   // Return the ascent as an approximation because Pango doesn't support cap
188   // height.
189   // TODO(yukishiino): Come up with a better approximation of cap height, or
190   // support cap height metrics.  Another option is to have a hard-coded table
191   // of cap height for major fonts used in Chromium/Chrome.
192   // See http://crbug.com/249507
193   return ascent_pixels_;
194 }
195 
GetAverageCharacterWidth() const196 int PlatformFontPango::GetAverageCharacterWidth() const {
197   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
198   return SkScalarRound(average_width_pixels_);
199 }
200 
GetStringWidth(const base::string16 & text) const201 int PlatformFontPango::GetStringWidth(const base::string16& text) const {
202   return Canvas::GetStringWidth(text,
203                                 Font(const_cast<PlatformFontPango*>(this)));
204 }
205 
GetExpectedTextWidth(int length) const206 int PlatformFontPango::GetExpectedTextWidth(int length) const {
207   double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth();
208   return round(static_cast<float>(length) * char_width);
209 }
210 
GetStyle() const211 int PlatformFontPango::GetStyle() const {
212   return style_;
213 }
214 
GetFontName() const215 std::string PlatformFontPango::GetFontName() const {
216   return font_family_;
217 }
218 
GetActualFontNameForTesting() const219 std::string PlatformFontPango::GetActualFontNameForTesting() const {
220   SkString family_name;
221   typeface_->getFamilyName(&family_name);
222   return family_name.c_str();
223 }
224 
GetFontSize() const225 int PlatformFontPango::GetFontSize() const {
226   return font_size_pixels_;
227 }
228 
GetNativeFont() const229 NativeFont PlatformFontPango::GetNativeFont() const {
230   PangoFontDescription* pfd = pango_font_description_new();
231   pango_font_description_set_family(pfd, GetFontName().c_str());
232   // Set the absolute size to avoid overflowing UI elements.
233   // pango_font_description_set_absolute_size() takes a size in Pango units.
234   // There are PANGO_SCALE Pango units in one device unit.  Screen output
235   // devices use pixels as their device units.
236   pango_font_description_set_absolute_size(
237       pfd, font_size_pixels_ * PANGO_SCALE);
238 
239   switch (GetStyle()) {
240     case gfx::Font::NORMAL:
241       // Nothing to do, should already be PANGO_STYLE_NORMAL.
242       break;
243     case gfx::Font::BOLD:
244       pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
245       break;
246     case gfx::Font::ITALIC:
247       pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
248       break;
249     case gfx::Font::UNDERLINE:
250       // TODO(deanm): How to do underline?  Where do we use it?  Probably have
251       // to paint it ourselves, see pango_font_metrics_get_underline_position.
252       break;
253   }
254 
255   return pfd;
256 }
257 
258 ////////////////////////////////////////////////////////////////////////////////
259 // PlatformFontPango, private:
260 
PlatformFontPango(const skia::RefPtr<SkTypeface> & typeface,const std::string & name,int size,int style)261 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface,
262                                      const std::string& name,
263                                      int size,
264                                      int style) {
265   InitWithTypefaceNameSizeAndStyle(typeface, name, size, style);
266 }
267 
~PlatformFontPango()268 PlatformFontPango::~PlatformFontPango() {}
269 
270 // static
GetDefaultFont()271 std::string PlatformFontPango::GetDefaultFont() {
272 #if !defined(TOOLKIT_GTK)
273 #if defined(OS_CHROMEOS)
274   // Font name must have been provided by way of SetDefaultFontDescription().
275   CHECK(default_font_description_);
276   return *default_font_description_;
277 #else
278   return "sans 10";
279 #endif    // defined(OS_CHROMEOS)
280 #else
281   GtkSettings* settings = gtk_settings_get_default();
282 
283   gchar* font_name = NULL;
284   g_object_get(settings, "gtk-font-name", &font_name, NULL);
285 
286   // Temporary CHECK for helping track down
287   // http://code.google.com/p/chromium/issues/detail?id=12530
288   CHECK(font_name) << " Unable to get gtk-font-name for default font.";
289 
290   std::string default_font = std::string(font_name);
291   g_free(font_name);
292   return default_font;
293 #endif  // !defined(TOOLKIT_GTK)
294 }
295 
296 
InitWithNameAndSize(const std::string & font_name,int font_size)297 void PlatformFontPango::InitWithNameAndSize(const std::string& font_name,
298                                             int font_size) {
299   DCHECK_GT(font_size, 0);
300   std::string fallback;
301 
302   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
303       SkTypeface::CreateFromName(font_name.c_str(), SkTypeface::kNormal));
304   if (!typeface) {
305     // A non-scalable font such as .pcf is specified. Falls back to a default
306     // scalable font.
307     typeface = skia::AdoptRef(
308         SkTypeface::CreateFromName(
309             kFallbackFontFamilyName, SkTypeface::kNormal));
310     CHECK(typeface) << "Could not find any font: "
311                     << font_name
312                     << ", " << kFallbackFontFamilyName;
313     fallback = kFallbackFontFamilyName;
314   }
315 
316   InitWithTypefaceNameSizeAndStyle(typeface,
317                                    fallback.empty() ? font_name : fallback,
318                                    font_size,
319                                    gfx::Font::NORMAL);
320 }
321 
InitWithTypefaceNameSizeAndStyle(const skia::RefPtr<SkTypeface> & typeface,const std::string & font_family,int font_size,int style)322 void PlatformFontPango::InitWithTypefaceNameSizeAndStyle(
323     const skia::RefPtr<SkTypeface>& typeface,
324     const std::string& font_family,
325     int font_size,
326     int style) {
327   typeface_ = typeface;
328   font_family_ = font_family;
329   font_size_pixels_ = font_size;
330   style_ = style;
331   pango_metrics_inited_ = false;
332   average_width_pixels_ = 0.0f;
333   underline_position_pixels_ = 0.0f;
334   underline_thickness_pixels_ = 0.0f;
335 
336   SkPaint paint;
337   SkPaint::FontMetrics metrics;
338   PaintSetup(&paint);
339   paint.getFontMetrics(&metrics);
340 
341   ascent_pixels_ = SkScalarCeil(-metrics.fAscent);
342   height_pixels_ = ascent_pixels_ + SkScalarCeil(metrics.fDescent);
343 }
344 
InitFromPlatformFont(const PlatformFontPango * other)345 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
346   typeface_ = other->typeface_;
347   font_family_ = other->font_family_;
348   font_size_pixels_ = other->font_size_pixels_;
349   style_ = other->style_;
350   height_pixels_ = other->height_pixels_;
351   ascent_pixels_ = other->ascent_pixels_;
352   pango_metrics_inited_ = other->pango_metrics_inited_;
353   average_width_pixels_ = other->average_width_pixels_;
354   underline_position_pixels_ = other->underline_position_pixels_;
355   underline_thickness_pixels_ = other->underline_thickness_pixels_;
356 }
357 
PaintSetup(SkPaint * paint) const358 void PlatformFontPango::PaintSetup(SkPaint* paint) const {
359   paint->setAntiAlias(false);
360   paint->setSubpixelText(false);
361   paint->setTextSize(font_size_pixels_);
362   paint->setTypeface(typeface_.get());
363   paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
364   paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
365                       -SK_Scalar1/4 : 0);
366 }
367 
InitPangoMetrics()368 void PlatformFontPango::InitPangoMetrics() {
369   if (!pango_metrics_inited_) {
370     pango_metrics_inited_ = true;
371     ScopedPangoFontDescription pango_desc(GetNativeFont());
372     PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
373 
374     underline_position_pixels_ =
375         pango_font_metrics_get_underline_position(pango_metrics) /
376         PANGO_SCALE;
377 
378     // TODO(davemoore): Come up with a better solution.
379     // This is a hack, but without doing this the underlines
380     // we get end up fuzzy. So we align to the midpoint of a pixel.
381     underline_position_pixels_ /= 2;
382 
383     underline_thickness_pixels_ =
384         pango_font_metrics_get_underline_thickness(pango_metrics) /
385         PANGO_SCALE;
386 
387     // First get the Pango-based width (converting from Pango units to pixels).
388     const double pango_width_pixels =
389         pango_font_metrics_get_approximate_char_width(pango_metrics) /
390         PANGO_SCALE;
391 
392     // Yes, this is how Microsoft recommends calculating the dialog unit
393     // conversions.
394     const int text_width_pixels = GetStringWidth(
395         ASCIIToUTF16("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
396     const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
397     average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
398   }
399 }
400 
GetAverageWidth() const401 double PlatformFontPango::GetAverageWidth() const {
402   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
403   return average_width_pixels_;
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 // PlatformFont, public:
408 
409 // static
CreateDefault()410 PlatformFont* PlatformFont::CreateDefault() {
411   return new PlatformFontPango;
412 }
413 
414 // static
CreateFromNativeFont(NativeFont native_font)415 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
416   return new PlatformFontPango(native_font);
417 }
418 
419 // static
CreateFromNameAndSize(const std::string & font_name,int font_size)420 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name,
421                                                   int font_size) {
422   return new PlatformFontPango(font_name, font_size);
423 }
424 
425 }  // namespace gfx
426