• 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/font_list.h"
23 #include "ui/gfx/linux_font_delegate.h"
24 #include "ui/gfx/pango_util.h"
25 #include "ui/gfx/text_utils.h"
26 
27 namespace {
28 
29 // The font family name which is used when a user's application font for
30 // GNOME/KDE is a non-scalable one. The name should be listed in the
31 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
32 const char* kFallbackFontFamilyName = "sans";
33 
34 // Returns the available font family that best (in FontConfig's eyes) matches
35 // the supplied list of family names.
FindBestMatchFontFamilyName(const std::vector<std::string> & family_names)36 std::string FindBestMatchFontFamilyName(
37     const std::vector<std::string>& family_names) {
38   FcPattern* pattern = FcPatternCreate();
39   for (std::vector<std::string>::const_iterator it = family_names.begin();
40        it != family_names.end(); ++it) {
41     FcValue fcvalue;
42     fcvalue.type = FcTypeString;
43     fcvalue.u.s = reinterpret_cast<const FcChar8*>(it->c_str());
44     FcPatternAdd(pattern, FC_FAMILY, fcvalue, FcTrue /* append */);
45   }
46 
47   FcConfigSubstitute(0, pattern, FcMatchPattern);
48   FcDefaultSubstitute(pattern);
49   FcResult result;
50   FcPattern* match = FcFontMatch(0, pattern, &result);
51   DCHECK(match) << "Could not find font";
52   FcChar8* match_family = NULL;
53   FcPatternGetString(match, FC_FAMILY, 0, &match_family);
54   std::string font_family(reinterpret_cast<char*>(match_family));
55   FcPatternDestroy(pattern);
56   FcPatternDestroy(match);
57   return font_family;
58 }
59 
60 }  // namespace
61 
62 namespace gfx {
63 
64 // static
65 Font* PlatformFontPango::default_font_ = NULL;
66 
67 #if defined(OS_CHROMEOS)
68 // static
69 std::string* PlatformFontPango::default_font_description_ = NULL;
70 #endif
71 
72 ////////////////////////////////////////////////////////////////////////////////
73 // PlatformFontPango, public:
74 
PlatformFontPango()75 PlatformFontPango::PlatformFontPango() {
76   if (default_font_ == NULL) {
77     std::string font_name = GetDefaultFont();
78 
79     ScopedPangoFontDescription desc(
80         pango_font_description_from_string(font_name.c_str()));
81     default_font_ = new Font(desc.get());
82 
83     DCHECK(default_font_);
84   }
85 
86   InitFromPlatformFont(
87       static_cast<PlatformFontPango*>(default_font_->platform_font()));
88 }
89 
PlatformFontPango(NativeFont native_font)90 PlatformFontPango::PlatformFontPango(NativeFont native_font) {
91   std::vector<std::string> family_names;
92   base::SplitString(pango_font_description_get_family(native_font), ',',
93                     &family_names);
94   std::string font_family = FindBestMatchFontFamilyName(family_names);
95   InitWithNameAndSize(font_family, gfx::GetPangoFontSizeInPixels(native_font));
96 
97   int style = 0;
98   if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) {
99     // TODO(davemoore) What should we do about other weights? We currently
100     // only support BOLD.
101     style |= gfx::Font::BOLD;
102   }
103   if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) {
104     // TODO(davemoore) What about PANGO_STYLE_OBLIQUE?
105     style |= gfx::Font::ITALIC;
106   }
107   if (style != 0)
108     style_ = style;
109 }
110 
PlatformFontPango(const std::string & font_name,int font_size)111 PlatformFontPango::PlatformFontPango(const std::string& font_name,
112                                      int font_size) {
113   InitWithNameAndSize(font_name, font_size);
114 }
115 
underline_position() const116 double PlatformFontPango::underline_position() const {
117   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
118   return underline_position_pixels_;
119 }
120 
underline_thickness() const121 double PlatformFontPango::underline_thickness() const {
122   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
123   return underline_thickness_pixels_;
124 }
125 
126 ////////////////////////////////////////////////////////////////////////////////
127 // PlatformFontPango, PlatformFont implementation:
128 
129 // static
ReloadDefaultFont()130 void PlatformFontPango::ReloadDefaultFont() {
131   delete default_font_;
132   default_font_ = NULL;
133 }
134 
135 #if defined(OS_CHROMEOS)
136 // static
SetDefaultFontDescription(const std::string & font_description)137 void PlatformFontPango::SetDefaultFontDescription(
138     const std::string& font_description) {
139   delete default_font_description_;
140   default_font_description_ = new std::string(font_description);
141 }
142 
143 #endif
144 
DeriveFont(int size_delta,int style) const145 Font PlatformFontPango::DeriveFont(int size_delta, int style) const {
146   // If the delta is negative, if must not push the size below 1
147   if (size_delta < 0)
148     DCHECK_LT(-size_delta, font_size_pixels_);
149 
150   if (style == style_) {
151     // Fast path, we just use the same typeface at a different size
152     return Font(new PlatformFontPango(typeface_,
153                                       font_family_,
154                                       font_size_pixels_ + size_delta,
155                                       style_));
156   }
157 
158   // If the style has changed we may need to load a new face
159   int skstyle = SkTypeface::kNormal;
160   if (gfx::Font::BOLD & style)
161     skstyle |= SkTypeface::kBold;
162   if (gfx::Font::ITALIC & style)
163     skstyle |= SkTypeface::kItalic;
164 
165   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
166       SkTypeface::CreateFromName(
167           font_family_.c_str(),
168           static_cast<SkTypeface::Style>(skstyle)));
169 
170   return Font(new PlatformFontPango(typeface,
171                                     font_family_,
172                                     font_size_pixels_ + size_delta,
173                                     style));
174 }
175 
GetHeight() const176 int PlatformFontPango::GetHeight() const {
177   return height_pixels_;
178 }
179 
GetBaseline() const180 int PlatformFontPango::GetBaseline() const {
181   return ascent_pixels_;
182 }
183 
GetCapHeight() const184 int PlatformFontPango::GetCapHeight() const {
185   return cap_height_pixels_;
186 }
187 
GetExpectedTextWidth(int length) const188 int PlatformFontPango::GetExpectedTextWidth(int length) const {
189   double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth();
190   return round(static_cast<float>(length) * char_width);
191 }
192 
GetStyle() const193 int PlatformFontPango::GetStyle() const {
194   return style_;
195 }
196 
GetFontName() const197 std::string PlatformFontPango::GetFontName() const {
198   return font_family_;
199 }
200 
GetActualFontNameForTesting() const201 std::string PlatformFontPango::GetActualFontNameForTesting() const {
202   SkString family_name;
203   typeface_->getFamilyName(&family_name);
204   return family_name.c_str();
205 }
206 
GetFontSize() const207 int PlatformFontPango::GetFontSize() const {
208   return font_size_pixels_;
209 }
210 
GetNativeFont() const211 NativeFont PlatformFontPango::GetNativeFont() const {
212   PangoFontDescription* pfd = pango_font_description_new();
213   pango_font_description_set_family(pfd, GetFontName().c_str());
214   // Set the absolute size to avoid overflowing UI elements.
215   // pango_font_description_set_absolute_size() takes a size in Pango units.
216   // There are PANGO_SCALE Pango units in one device unit.  Screen output
217   // devices use pixels as their device units.
218   pango_font_description_set_absolute_size(
219       pfd, font_size_pixels_ * PANGO_SCALE);
220 
221   switch (GetStyle()) {
222     case gfx::Font::NORMAL:
223       // Nothing to do, should already be PANGO_STYLE_NORMAL.
224       break;
225     case gfx::Font::BOLD:
226       pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
227       break;
228     case gfx::Font::ITALIC:
229       pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
230       break;
231     case gfx::Font::UNDERLINE:
232       // TODO(deanm): How to do underline?  Where do we use it?  Probably have
233       // to paint it ourselves, see pango_font_metrics_get_underline_position.
234       break;
235   }
236 
237   return pfd;
238 }
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 // PlatformFontPango, private:
242 
PlatformFontPango(const skia::RefPtr<SkTypeface> & typeface,const std::string & name,int size,int style)243 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface,
244                                      const std::string& name,
245                                      int size,
246                                      int style) {
247   InitWithTypefaceNameSizeAndStyle(typeface, name, size, style);
248 }
249 
~PlatformFontPango()250 PlatformFontPango::~PlatformFontPango() {}
251 
252 // static
GetDefaultFont()253 std::string PlatformFontPango::GetDefaultFont() {
254 #if defined(OS_CHROMEOS)
255   // Font name must have been provided by way of SetDefaultFontDescription().
256   CHECK(default_font_description_);
257   return *default_font_description_;
258 #else
259   const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance();
260   if (delegate)
261     return delegate->GetDefaultFontName();
262 
263   return "sans 10";
264 #endif    // defined(OS_CHROMEOS)
265 }
266 
InitWithNameAndSize(const std::string & font_name,int font_size)267 void PlatformFontPango::InitWithNameAndSize(const std::string& font_name,
268                                             int font_size) {
269   DCHECK_GT(font_size, 0);
270   std::string fallback;
271 
272   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
273       SkTypeface::CreateFromName(font_name.c_str(), SkTypeface::kNormal));
274   if (!typeface) {
275     // A non-scalable font such as .pcf is specified. Falls back to a default
276     // scalable font.
277     typeface = skia::AdoptRef(
278         SkTypeface::CreateFromName(
279             kFallbackFontFamilyName, SkTypeface::kNormal));
280     CHECK(typeface) << "Could not find any font: "
281                     << font_name
282                     << ", " << kFallbackFontFamilyName;
283     fallback = kFallbackFontFamilyName;
284   }
285 
286   InitWithTypefaceNameSizeAndStyle(typeface,
287                                    fallback.empty() ? font_name : fallback,
288                                    font_size,
289                                    gfx::Font::NORMAL);
290 }
291 
InitWithTypefaceNameSizeAndStyle(const skia::RefPtr<SkTypeface> & typeface,const std::string & font_family,int font_size,int style)292 void PlatformFontPango::InitWithTypefaceNameSizeAndStyle(
293     const skia::RefPtr<SkTypeface>& typeface,
294     const std::string& font_family,
295     int font_size,
296     int style) {
297   typeface_ = typeface;
298   font_family_ = font_family;
299   font_size_pixels_ = font_size;
300   style_ = style;
301   pango_metrics_inited_ = false;
302   average_width_pixels_ = 0.0f;
303   underline_position_pixels_ = 0.0f;
304   underline_thickness_pixels_ = 0.0f;
305 
306   SkPaint paint;
307   SkPaint::FontMetrics metrics;
308   PaintSetup(&paint);
309   paint.getFontMetrics(&metrics);
310 
311   ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent);
312   height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent);
313   cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight);
314 }
315 
InitFromPlatformFont(const PlatformFontPango * other)316 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
317   typeface_ = other->typeface_;
318   font_family_ = other->font_family_;
319   font_size_pixels_ = other->font_size_pixels_;
320   style_ = other->style_;
321   height_pixels_ = other->height_pixels_;
322   ascent_pixels_ = other->ascent_pixels_;
323   cap_height_pixels_ = other->cap_height_pixels_;
324   pango_metrics_inited_ = other->pango_metrics_inited_;
325   average_width_pixels_ = other->average_width_pixels_;
326   underline_position_pixels_ = other->underline_position_pixels_;
327   underline_thickness_pixels_ = other->underline_thickness_pixels_;
328 }
329 
PaintSetup(SkPaint * paint) const330 void PlatformFontPango::PaintSetup(SkPaint* paint) const {
331   paint->setAntiAlias(false);
332   paint->setSubpixelText(false);
333   paint->setTextSize(font_size_pixels_);
334   paint->setTypeface(typeface_.get());
335   paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
336   paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
337                       -SK_Scalar1/4 : 0);
338 }
339 
InitPangoMetrics()340 void PlatformFontPango::InitPangoMetrics() {
341   if (!pango_metrics_inited_) {
342     pango_metrics_inited_ = true;
343     ScopedPangoFontDescription pango_desc(GetNativeFont());
344     PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
345 
346     underline_position_pixels_ =
347         pango_font_metrics_get_underline_position(pango_metrics) /
348         PANGO_SCALE;
349 
350     // TODO(davemoore): Come up with a better solution.
351     // This is a hack, but without doing this the underlines
352     // we get end up fuzzy. So we align to the midpoint of a pixel.
353     underline_position_pixels_ /= 2;
354 
355     underline_thickness_pixels_ =
356         pango_font_metrics_get_underline_thickness(pango_metrics) /
357         PANGO_SCALE;
358 
359     // First get the Pango-based width (converting from Pango units to pixels).
360     const double pango_width_pixels =
361         pango_font_metrics_get_approximate_char_width(pango_metrics) /
362         PANGO_SCALE;
363 
364     // Yes, this is how Microsoft recommends calculating the dialog unit
365     // conversions.
366     const int text_width_pixels = GetStringWidth(
367         base::ASCIIToUTF16(
368             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
369         FontList(Font(this)));
370     const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
371     average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
372   }
373 }
374 
GetAverageWidth() const375 double PlatformFontPango::GetAverageWidth() const {
376   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
377   return average_width_pixels_;
378 }
379 
380 ////////////////////////////////////////////////////////////////////////////////
381 // PlatformFont, public:
382 
383 // static
CreateDefault()384 PlatformFont* PlatformFont::CreateDefault() {
385   return new PlatformFontPango;
386 }
387 
388 // static
CreateFromNativeFont(NativeFont native_font)389 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
390   return new PlatformFontPango(native_font);
391 }
392 
393 // static
CreateFromNameAndSize(const std::string & font_name,int font_size)394 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name,
395                                                   int font_size) {
396   return new PlatformFontPango(font_name, font_size);
397 }
398 
399 }  // namespace gfx
400