403b8364bce918dfbf130e97de1b9ebd43b57039
[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 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 // Creates a SkTypeface for the passed-in Font::FontStyle and family. If a
35 // fallback typeface is used instead of the requested family, |family| will be
36 // updated to contain the fallback's family name.
37 skia::RefPtr<SkTypeface> CreateSkTypeface(int style, std::string* family) {
38   DCHECK(family);
39
40   int skia_style = SkTypeface::kNormal;
41   if (gfx::Font::BOLD & style)
42     skia_style |= SkTypeface::kBold;
43   if (gfx::Font::ITALIC & style)
44     skia_style |= SkTypeface::kItalic;
45
46   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(SkTypeface::CreateFromName(
47       family->c_str(), static_cast<SkTypeface::Style>(skia_style)));
48   if (!typeface) {
49     // A non-scalable font such as .pcf is specified. Fall back to a default
50     // scalable font.
51     typeface = skia::AdoptRef(SkTypeface::CreateFromName(
52         kFallbackFontFamilyName, static_cast<SkTypeface::Style>(skia_style)));
53     CHECK(typeface) << "Could not find any font: " << family << ", "
54                     << kFallbackFontFamilyName;
55     *family = kFallbackFontFamilyName;
56   }
57   return typeface;
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
75 PlatformFontPango::PlatformFontPango() {
76   if (!default_font_) {
77     scoped_ptr<ScopedPangoFontDescription> description;
78 #if defined(OS_CHROMEOS)
79     CHECK(default_font_description_);
80     description.reset(
81         new ScopedPangoFontDescription(*default_font_description_));
82 #else
83     const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance();
84     if (delegate)
85       description = delegate->GetDefaultPangoFontDescription();
86 #endif
87     if (!description || !description->get())
88       description.reset(new ScopedPangoFontDescription("sans 10"));
89     default_font_ = new Font(description->get());
90   }
91
92   InitFromPlatformFont(
93       static_cast<PlatformFontPango*>(default_font_->platform_font()));
94 }
95
96 PlatformFontPango::PlatformFontPango(NativeFont native_font) {
97   FontRenderParamsQuery query(false);
98   base::SplitString(pango_font_description_get_family(native_font), ',',
99                     &query.families);
100
101   const int pango_size =
102       pango_font_description_get_size(native_font) / PANGO_SCALE;
103   if (pango_font_description_get_size_is_absolute(native_font))
104     query.pixel_size = pango_size;
105   else
106     query.point_size = pango_size;
107
108   query.style = gfx::Font::NORMAL;
109   // TODO(davemoore): Support weights other than bold?
110   if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD)
111     query.style |= gfx::Font::BOLD;
112   // TODO(davemoore): What about PANGO_STYLE_OBLIQUE?
113   if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC)
114     query.style |= gfx::Font::ITALIC;
115
116   std::string font_family;
117   const FontRenderParams params = gfx::GetFontRenderParams(query, &font_family);
118   InitFromDetails(skia::RefPtr<SkTypeface>(), font_family,
119                   gfx::GetPangoFontSizeInPixels(native_font),
120                   query.style, params);
121 }
122
123 PlatformFontPango::PlatformFontPango(const std::string& font_name,
124                                      int font_size_pixels) {
125   FontRenderParamsQuery query(false);
126   query.families.push_back(font_name);
127   query.pixel_size = font_size_pixels;
128   query.style = gfx::Font::NORMAL;
129   InitFromDetails(skia::RefPtr<SkTypeface>(), font_name, font_size_pixels,
130                   query.style, gfx::GetFontRenderParams(query, NULL));
131 }
132
133 double PlatformFontPango::underline_position() const {
134   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
135   return underline_position_pixels_;
136 }
137
138 double PlatformFontPango::underline_thickness() const {
139   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
140   return underline_thickness_pixels_;
141 }
142
143 ////////////////////////////////////////////////////////////////////////////////
144 // PlatformFontPango, PlatformFont implementation:
145
146 // static
147 void PlatformFontPango::ReloadDefaultFont() {
148   delete default_font_;
149   default_font_ = NULL;
150 }
151
152 #if defined(OS_CHROMEOS)
153 // static
154 void PlatformFontPango::SetDefaultFontDescription(
155     const std::string& font_description) {
156   delete default_font_description_;
157   default_font_description_ = new std::string(font_description);
158 }
159
160 #endif
161
162 Font PlatformFontPango::DeriveFont(int size_delta, int style) const {
163   const int new_size = font_size_pixels_ + size_delta;
164   DCHECK_GT(new_size, 0);
165
166   // If the style changed, we may need to load a new face.
167   std::string new_family = font_family_;
168   skia::RefPtr<SkTypeface> typeface =
169       (style == style_) ? typeface_ : CreateSkTypeface(style, &new_family);
170
171   FontRenderParamsQuery query(false);
172   query.families.push_back(new_family);
173   query.pixel_size = new_size;
174   query.style = style;
175
176   return Font(new PlatformFontPango(typeface, new_family, new_size, style,
177                                     gfx::GetFontRenderParams(query, NULL)));
178 }
179
180 int PlatformFontPango::GetHeight() const {
181   return height_pixels_;
182 }
183
184 int PlatformFontPango::GetBaseline() const {
185   return ascent_pixels_;
186 }
187
188 int PlatformFontPango::GetCapHeight() const {
189   return cap_height_pixels_;
190 }
191
192 int PlatformFontPango::GetExpectedTextWidth(int length) const {
193   double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth();
194   return round(static_cast<float>(length) * char_width);
195 }
196
197 int PlatformFontPango::GetStyle() const {
198   return style_;
199 }
200
201 std::string PlatformFontPango::GetFontName() const {
202   return font_family_;
203 }
204
205 std::string PlatformFontPango::GetActualFontNameForTesting() const {
206   SkString family_name;
207   typeface_->getFamilyName(&family_name);
208   return family_name.c_str();
209 }
210
211 int PlatformFontPango::GetFontSize() const {
212   return font_size_pixels_;
213 }
214
215 const FontRenderParams& PlatformFontPango::GetFontRenderParams() const {
216   return font_render_params_;
217 }
218
219 NativeFont PlatformFontPango::GetNativeFont() const {
220   PangoFontDescription* pfd = pango_font_description_new();
221   pango_font_description_set_family(pfd, GetFontName().c_str());
222   // Set the absolute size to avoid overflowing UI elements.
223   // pango_font_description_set_absolute_size() takes a size in Pango units.
224   // There are PANGO_SCALE Pango units in one device unit.  Screen output
225   // devices use pixels as their device units.
226   pango_font_description_set_absolute_size(
227       pfd, font_size_pixels_ * PANGO_SCALE);
228
229   switch (GetStyle()) {
230     case gfx::Font::NORMAL:
231       // Nothing to do, should already be PANGO_STYLE_NORMAL.
232       break;
233     case gfx::Font::BOLD:
234       pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
235       break;
236     case gfx::Font::ITALIC:
237       pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
238       break;
239     case gfx::Font::UNDERLINE:
240       // TODO(deanm): How to do underline?  Where do we use it?  Probably have
241       // to paint it ourselves, see pango_font_metrics_get_underline_position.
242       break;
243   }
244
245   return pfd;
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // PlatformFontPango, private:
250
251 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface,
252                                      const std::string& name,
253                                      int size_pixels,
254                                      int style,
255                                      const FontRenderParams& render_params) {
256   InitFromDetails(typeface, name, size_pixels, style, render_params);
257 }
258
259 PlatformFontPango::~PlatformFontPango() {}
260
261 void PlatformFontPango::InitFromDetails(
262     const skia::RefPtr<SkTypeface>& typeface,
263     const std::string& font_family,
264     int font_size_pixels,
265     int style,
266     const FontRenderParams& render_params) {
267   DCHECK_GT(font_size_pixels, 0);
268
269   font_family_ = font_family;
270   typeface_ = typeface ? typeface : CreateSkTypeface(style, &font_family_);
271
272   font_size_pixels_ = font_size_pixels;
273   style_ = style;
274   font_render_params_ = render_params;
275
276   SkPaint paint;
277   SkPaint::FontMetrics metrics;
278   PaintSetup(&paint);
279   paint.getFontMetrics(&metrics);
280   ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent);
281   height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent);
282   cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight);
283
284   pango_metrics_inited_ = false;
285   average_width_pixels_ = 0.0f;
286   underline_position_pixels_ = 0.0f;
287   underline_thickness_pixels_ = 0.0f;
288 }
289
290 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
291   typeface_ = other->typeface_;
292   font_family_ = other->font_family_;
293   font_size_pixels_ = other->font_size_pixels_;
294   style_ = other->style_;
295   font_render_params_ = other->font_render_params_;
296   ascent_pixels_ = other->ascent_pixels_;
297   height_pixels_ = other->height_pixels_;
298   cap_height_pixels_ = other->cap_height_pixels_;
299   pango_metrics_inited_ = other->pango_metrics_inited_;
300   average_width_pixels_ = other->average_width_pixels_;
301   underline_position_pixels_ = other->underline_position_pixels_;
302   underline_thickness_pixels_ = other->underline_thickness_pixels_;
303 }
304
305 void PlatformFontPango::PaintSetup(SkPaint* paint) const {
306   paint->setAntiAlias(false);
307   paint->setSubpixelText(false);
308   paint->setTextSize(font_size_pixels_);
309   paint->setTypeface(typeface_.get());
310   paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
311   paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
312                       -SK_Scalar1/4 : 0);
313 }
314
315 void PlatformFontPango::InitPangoMetrics() {
316   if (!pango_metrics_inited_) {
317     pango_metrics_inited_ = true;
318     ScopedPangoFontDescription pango_desc(GetNativeFont());
319     PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
320
321     underline_position_pixels_ =
322         pango_font_metrics_get_underline_position(pango_metrics) /
323         PANGO_SCALE;
324
325     // TODO(davemoore): Come up with a better solution.
326     // This is a hack, but without doing this the underlines
327     // we get end up fuzzy. So we align to the midpoint of a pixel.
328     underline_position_pixels_ /= 2;
329
330     underline_thickness_pixels_ =
331         pango_font_metrics_get_underline_thickness(pango_metrics) /
332         PANGO_SCALE;
333
334     // First get the Pango-based width (converting from Pango units to pixels).
335     const double pango_width_pixels =
336         pango_font_metrics_get_approximate_char_width(pango_metrics) /
337         PANGO_SCALE;
338
339     // Yes, this is how Microsoft recommends calculating the dialog unit
340     // conversions.
341     const int text_width_pixels = GetStringWidth(
342         base::ASCIIToUTF16(
343             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
344         FontList(Font(this)));
345     const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
346     average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
347   }
348 }
349
350 double PlatformFontPango::GetAverageWidth() const {
351   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
352   return average_width_pixels_;
353 }
354
355 ////////////////////////////////////////////////////////////////////////////////
356 // PlatformFont, public:
357
358 // static
359 PlatformFont* PlatformFont::CreateDefault() {
360   return new PlatformFontPango;
361 }
362
363 // static
364 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
365   return new PlatformFontPango(native_font);
366 }
367
368 // static
369 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name,
370                                                   int font_size) {
371   return new PlatformFontPango(font_name, font_size);
372 }
373
374 }  // namespace gfx