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.
5 #include "ui/gfx/platform_font_pango.h"
7 #include <fontconfig/fontconfig.h>
8 #include <pango/pango.h>
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"
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";
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) {
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;
46 skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(SkTypeface::CreateFromName(
47 family->c_str(), static_cast<SkTypeface::Style>(skia_style)));
49 // A non-scalable font such as .pcf is specified. Fall back to a default
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;
65 Font* PlatformFontPango::default_font_ = NULL;
67 #if defined(OS_CHROMEOS)
69 std::string* PlatformFontPango::default_font_description_ = NULL;
72 ////////////////////////////////////////////////////////////////////////////////
73 // PlatformFontPango, public:
75 PlatformFontPango::PlatformFontPango() {
77 scoped_ptr<ScopedPangoFontDescription> description;
78 #if defined(OS_CHROMEOS)
79 CHECK(default_font_description_);
81 new ScopedPangoFontDescription(*default_font_description_));
83 const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance();
85 description = delegate->GetDefaultPangoFontDescription();
87 if (!description || !description->get())
88 description.reset(new ScopedPangoFontDescription("sans 10"));
89 default_font_ = new Font(description->get());
93 static_cast<PlatformFontPango*>(default_font_->platform_font()));
96 PlatformFontPango::PlatformFontPango(NativeFont native_font) {
97 FontRenderParamsQuery query(false);
98 base::SplitString(pango_font_description_get_family(native_font), ',',
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;
106 query.point_size = pango_size;
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;
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);
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));
133 ////////////////////////////////////////////////////////////////////////////////
134 // PlatformFontPango, PlatformFont implementation:
137 void PlatformFontPango::ReloadDefaultFont() {
138 delete default_font_;
139 default_font_ = NULL;
142 #if defined(OS_CHROMEOS)
144 void PlatformFontPango::SetDefaultFontDescription(
145 const std::string& font_description) {
146 delete default_font_description_;
147 default_font_description_ = new std::string(font_description);
152 Font PlatformFontPango::DeriveFont(int size_delta, int style) const {
153 const int new_size = font_size_pixels_ + size_delta;
154 DCHECK_GT(new_size, 0);
156 // If the style changed, we may need to load a new face.
157 std::string new_family = font_family_;
158 skia::RefPtr<SkTypeface> typeface =
159 (style == style_) ? typeface_ : CreateSkTypeface(style, &new_family);
161 FontRenderParamsQuery query(false);
162 query.families.push_back(new_family);
163 query.pixel_size = new_size;
166 return Font(new PlatformFontPango(typeface, new_family, new_size, style,
167 gfx::GetFontRenderParams(query, NULL)));
170 int PlatformFontPango::GetHeight() const {
171 return height_pixels_;
174 int PlatformFontPango::GetBaseline() const {
175 return ascent_pixels_;
178 int PlatformFontPango::GetCapHeight() const {
179 return cap_height_pixels_;
182 int PlatformFontPango::GetExpectedTextWidth(int length) const {
183 double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth();
184 return round(static_cast<float>(length) * char_width);
187 int PlatformFontPango::GetStyle() const {
191 std::string PlatformFontPango::GetFontName() const {
195 std::string PlatformFontPango::GetActualFontNameForTesting() const {
196 SkString family_name;
197 typeface_->getFamilyName(&family_name);
198 return family_name.c_str();
201 int PlatformFontPango::GetFontSize() const {
202 return font_size_pixels_;
205 const FontRenderParams& PlatformFontPango::GetFontRenderParams() const {
206 return font_render_params_;
209 NativeFont PlatformFontPango::GetNativeFont() const {
210 PangoFontDescription* pfd = pango_font_description_new();
211 pango_font_description_set_family(pfd, GetFontName().c_str());
212 // Set the absolute size to avoid overflowing UI elements.
213 // pango_font_description_set_absolute_size() takes a size in Pango units.
214 // There are PANGO_SCALE Pango units in one device unit. Screen output
215 // devices use pixels as their device units.
216 pango_font_description_set_absolute_size(
217 pfd, font_size_pixels_ * PANGO_SCALE);
219 switch (GetStyle()) {
220 case gfx::Font::NORMAL:
221 // Nothing to do, should already be PANGO_STYLE_NORMAL.
223 case gfx::Font::BOLD:
224 pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
226 case gfx::Font::ITALIC:
227 pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
229 case gfx::Font::UNDERLINE:
230 // TODO(deanm): How to do underline? Where do we use it? Probably have
231 // to paint it ourselves, see pango_font_metrics_get_underline_position.
238 ////////////////////////////////////////////////////////////////////////////////
239 // PlatformFontPango, private:
241 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface,
242 const std::string& name,
245 const FontRenderParams& render_params) {
246 InitFromDetails(typeface, name, size_pixels, style, render_params);
249 PlatformFontPango::~PlatformFontPango() {}
251 void PlatformFontPango::InitFromDetails(
252 const skia::RefPtr<SkTypeface>& typeface,
253 const std::string& font_family,
254 int font_size_pixels,
256 const FontRenderParams& render_params) {
257 DCHECK_GT(font_size_pixels, 0);
259 font_family_ = font_family;
260 typeface_ = typeface ? typeface : CreateSkTypeface(style, &font_family_);
262 font_size_pixels_ = font_size_pixels;
264 font_render_params_ = render_params;
267 SkPaint::FontMetrics metrics;
269 paint.getFontMetrics(&metrics);
270 ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent);
271 height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent);
272 cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight);
274 pango_metrics_inited_ = false;
275 average_width_pixels_ = 0.0f;
278 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
279 typeface_ = other->typeface_;
280 font_family_ = other->font_family_;
281 font_size_pixels_ = other->font_size_pixels_;
282 style_ = other->style_;
283 font_render_params_ = other->font_render_params_;
284 ascent_pixels_ = other->ascent_pixels_;
285 height_pixels_ = other->height_pixels_;
286 cap_height_pixels_ = other->cap_height_pixels_;
287 pango_metrics_inited_ = other->pango_metrics_inited_;
288 average_width_pixels_ = other->average_width_pixels_;
291 void PlatformFontPango::PaintSetup(SkPaint* paint) const {
292 paint->setAntiAlias(false);
293 paint->setSubpixelText(false);
294 paint->setTextSize(font_size_pixels_);
295 paint->setTypeface(typeface_.get());
296 paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
297 paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
301 void PlatformFontPango::InitPangoMetrics() {
302 if (!pango_metrics_inited_) {
303 pango_metrics_inited_ = true;
304 ScopedPangoFontDescription pango_desc(GetNativeFont());
305 PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
307 // First get the Pango-based width (converting from Pango units to pixels).
308 const double pango_width_pixels =
309 pango_font_metrics_get_approximate_char_width(pango_metrics) /
312 // Yes, this is how Microsoft recommends calculating the dialog unit
314 const int text_width_pixels = GetStringWidth(
316 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
317 FontList(Font(this)));
318 const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
319 average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
323 double PlatformFontPango::GetAverageWidth() const {
324 const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
325 return average_width_pixels_;
328 ////////////////////////////////////////////////////////////////////////////////
329 // PlatformFont, public:
332 PlatformFont* PlatformFont::CreateDefault() {
333 return new PlatformFontPango;
337 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
338 return new PlatformFontPango(native_font);
342 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name,
344 return new PlatformFontPango(font_name, font_size);