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