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