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 double PlatformFontPango::underline_position() const {
134 const_cast<PlatformFontPango
*>(this)->InitPangoMetrics();
135 return underline_position_pixels_
;
138 double PlatformFontPango::underline_thickness() const {
139 const_cast<PlatformFontPango
*>(this)->InitPangoMetrics();
140 return underline_thickness_pixels_
;
143 ////////////////////////////////////////////////////////////////////////////////
144 // PlatformFontPango, PlatformFont implementation:
147 void PlatformFontPango::ReloadDefaultFont() {
148 delete default_font_
;
149 default_font_
= NULL
;
152 #if defined(OS_CHROMEOS)
154 void PlatformFontPango::SetDefaultFontDescription(
155 const std::string
& font_description
) {
156 delete default_font_description_
;
157 default_font_description_
= new std::string(font_description
);
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);
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
);
171 FontRenderParamsQuery
query(false);
172 query
.families
.push_back(new_family
);
173 query
.pixel_size
= new_size
;
176 return Font(new PlatformFontPango(typeface
, new_family
, new_size
, style
,
177 gfx::GetFontRenderParams(query
, NULL
)));
180 int PlatformFontPango::GetHeight() const {
181 return height_pixels_
;
184 int PlatformFontPango::GetBaseline() const {
185 return ascent_pixels_
;
188 int PlatformFontPango::GetCapHeight() const {
189 return cap_height_pixels_
;
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
);
197 int PlatformFontPango::GetStyle() const {
201 std::string
PlatformFontPango::GetFontName() const {
205 std::string
PlatformFontPango::GetActualFontNameForTesting() const {
206 SkString family_name
;
207 typeface_
->getFamilyName(&family_name
);
208 return family_name
.c_str();
211 int PlatformFontPango::GetFontSize() const {
212 return font_size_pixels_
;
215 const FontRenderParams
& PlatformFontPango::GetFontRenderParams() const {
216 return font_render_params_
;
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
);
229 switch (GetStyle()) {
230 case gfx::Font::NORMAL
:
231 // Nothing to do, should already be PANGO_STYLE_NORMAL.
233 case gfx::Font::BOLD
:
234 pango_font_description_set_weight(pfd
, PANGO_WEIGHT_BOLD
);
236 case gfx::Font::ITALIC
:
237 pango_font_description_set_style(pfd
, PANGO_STYLE_ITALIC
);
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.
248 ////////////////////////////////////////////////////////////////////////////////
249 // PlatformFontPango, private:
251 PlatformFontPango::PlatformFontPango(const skia::RefPtr
<SkTypeface
>& typeface
,
252 const std::string
& name
,
255 const FontRenderParams
& render_params
) {
256 InitFromDetails(typeface
, name
, size_pixels
, style
, render_params
);
259 PlatformFontPango::~PlatformFontPango() {}
261 void PlatformFontPango::InitFromDetails(
262 const skia::RefPtr
<SkTypeface
>& typeface
,
263 const std::string
& font_family
,
264 int font_size_pixels
,
266 const FontRenderParams
& render_params
) {
267 DCHECK_GT(font_size_pixels
, 0);
269 font_family_
= font_family
;
270 typeface_
= typeface
? typeface
: CreateSkTypeface(style
, &font_family_
);
272 font_size_pixels_
= font_size_pixels
;
274 font_render_params_
= render_params
;
277 SkPaint::FontMetrics metrics
;
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
);
284 pango_metrics_inited_
= false;
285 average_width_pixels_
= 0.0f
;
286 underline_position_pixels_
= 0.0f
;
287 underline_thickness_pixels_
= 0.0f
;
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_
;
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() ?
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());
321 underline_position_pixels_
=
322 pango_font_metrics_get_underline_position(pango_metrics
) /
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;
330 underline_thickness_pixels_
=
331 pango_font_metrics_get_underline_thickness(pango_metrics
) /
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
) /
339 // Yes, this is how Microsoft recommends calculating the dialog unit
341 const int text_width_pixels
= GetStringWidth(
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
);
350 double PlatformFontPango::GetAverageWidth() const {
351 const_cast<PlatformFontPango
*>(this)->InitPangoMetrics();
352 return average_width_pixels_
;
355 ////////////////////////////////////////////////////////////////////////////////
356 // PlatformFont, public:
359 PlatformFont
* PlatformFont::CreateDefault() {
360 return new PlatformFontPango
;
364 PlatformFont
* PlatformFont::CreateFromNativeFont(NativeFont native_font
) {
365 return new PlatformFontPango(native_font
);
369 PlatformFont
* PlatformFont::CreateFromNameAndSize(const std::string
& font_name
,
371 return new PlatformFontPango(font_name
, font_size
);