1 // Copyright (c) 2006-2008 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 "app/gfx/font.h"
9 #include <pango/pango.h>
11 #include "app/gfx/canvas.h"
12 #include "base/logging.h"
13 #include "base/string_piece.h"
14 #include "base/sys_string_conversions.h"
15 #include "third_party/skia/include/core/SkTypeface.h"
16 #include "third_party/skia/include/core/SkPaint.h"
20 // The font family name which is used when a user's application font for
21 // GNOME/KDE is a non-scalable one. The name should be listed in the
22 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
23 const char* kFallbackFontFamilyName
= "sans";
25 // Pango scales font sizes. This returns the scale factor. See
26 // pango_cairo_context_set_resolution for details.
27 // NOTE: this isn't entirely accurate, in that Pango also consults the
28 // FC_PIXEL_SIZE first (see get_font_size in pangocairo-fcfont), but this
29 // seems to give us the same sizes as used by Pango for all our fonts in both
31 static double GetPangoScaleFactor() {
32 static float scale_factor
= 0;
33 static bool determined_scale
= false;
34 if (!determined_scale
) {
35 PangoContext
* context
= gdk_pango_context_get();
36 scale_factor
= pango_cairo_context_get_resolution(context
);
37 g_object_unref(context
);
38 if (scale_factor
<= 0)
42 determined_scale
= true;
47 // Retrieves the pango metrics for a pango font description. Caches the metrics
48 // and never frees them. The metrics objects are relatively small and
49 // very expensive to look up.
50 static PangoFontMetrics
* GetPangoFontMetrics(PangoFontDescription
* desc
) {
51 static std::map
<int, PangoFontMetrics
*>* desc_to_metrics
= NULL
;
52 static PangoContext
* context
= NULL
;
55 context
= gdk_pango_context_get_for_screen(gdk_screen_get_default());
56 pango_context_set_language(context
, pango_language_get_default());
59 if (!desc_to_metrics
) {
60 desc_to_metrics
= new std::map
<int, PangoFontMetrics
*>();
63 int desc_hash
= pango_font_description_hash(desc
);
64 std::map
<int, PangoFontMetrics
*>::iterator i
=
65 desc_to_metrics
->find(desc_hash
);
67 if (i
== desc_to_metrics
->end()) {
68 PangoFontMetrics
* metrics
= pango_context_get_metrics(context
, desc
, NULL
);
69 (*desc_to_metrics
)[desc_hash
] = metrics
;
80 Font::Font(const Font
& other
) {
84 Font
& Font::operator=(const Font
& other
) {
89 Font::Font(SkTypeface
* tf
, const std::wstring
& font_family
, int font_size
,
91 : typeface_helper_(new SkAutoUnref(tf
)),
93 font_family_(font_family
),
94 font_size_(font_size
),
96 pango_metrics_inited_(false),
98 underline_position_(0.0),
99 underline_thickness_(0.0) {
104 void Font::calculateMetrics() {
106 SkPaint::FontMetrics metrics
;
108 paint
.getFontMetrics(&metrics
);
110 ascent_
= SkScalarCeil(-metrics
.fAscent
);
111 height_
= ascent_
+ SkScalarCeil(metrics
.fDescent
);
115 void Font::CopyFont(const Font
& other
) {
116 typeface_helper_
.reset(new SkAutoUnref(other
.typeface_
));
117 typeface_
= other
.typeface_
;
119 font_family_
= other
.font_family_
;
120 font_size_
= other
.font_size_
;
121 style_
= other
.style_
;
122 height_
= other
.height_
;
123 ascent_
= other
.ascent_
;
124 pango_metrics_inited_
= other
.pango_metrics_inited_
;
125 avg_width_
= other
.avg_width_
;
126 underline_position_
= other
.underline_position_
;
127 underline_thickness_
= other
.underline_thickness_
;
130 int Font::height() const {
134 int Font::baseline() const {
138 int Font::ave_char_width() const {
139 return SkScalarRound(avg_width());
142 Font
Font::CreateFont(const std::wstring
& font_family
, int font_size
) {
143 DCHECK_GT(font_size
, 0);
144 std::wstring fallback
;
146 SkTypeface
* tf
= SkTypeface::CreateFromName(
147 base::SysWideToUTF8(font_family
).c_str(), SkTypeface::kNormal
);
149 // A non-scalable font such as .pcf is specified. Falls back to a default
151 tf
= SkTypeface::CreateFromName(
152 kFallbackFontFamilyName
, SkTypeface::kNormal
);
153 CHECK(tf
) << "Could not find any font: "
154 << base::SysWideToUTF8(font_family
)
155 << ", " << kFallbackFontFamilyName
;
156 fallback
= base::SysUTF8ToWide(kFallbackFontFamilyName
);
158 SkAutoUnref
tf_helper(tf
);
161 tf
, fallback
.empty() ? font_family
: fallback
, font_size
, NORMAL
);
164 Font
Font::DeriveFont(int size_delta
, int style
) const {
165 // If the delta is negative, if must not push the size below 1
166 if (size_delta
< 0) {
167 DCHECK_LT(-size_delta
, font_size_
);
170 if (style
== style_
) {
171 // Fast path, we just use the same typeface at a different size
172 return Font(typeface_
, font_family_
, font_size_
+ size_delta
, style_
);
175 // If the style has changed we may need to load a new face
176 int skstyle
= SkTypeface::kNormal
;
178 skstyle
|= SkTypeface::kBold
;
180 skstyle
|= SkTypeface::kItalic
;
182 SkTypeface
* tf
= SkTypeface::CreateFromName(
183 base::SysWideToUTF8(font_family_
).c_str(),
184 static_cast<SkTypeface::Style
>(skstyle
));
185 SkAutoUnref
tf_helper(tf
);
187 return Font(tf
, font_family_
, font_size_
+ size_delta
, style
);
190 void Font::PaintSetup(SkPaint
* paint
) const {
191 paint
->setAntiAlias(false);
192 paint
->setSubpixelText(false);
193 paint
->setTextSize(SkFloatToScalar(font_size_
* GetPangoScaleFactor()));
194 paint
->setTypeface(typeface_
);
195 paint
->setFakeBoldText((BOLD
& style_
) && !typeface_
->isBold());
196 paint
->setTextSkewX((ITALIC
& style_
) && !typeface_
->isItalic() ?
200 int Font::GetStringWidth(const std::wstring
& text
) const {
201 int width
= 0, height
= 0;
203 Canvas::SizeStringInt(text
, *this, &width
, &height
, 0);
207 void Font::InitPangoMetrics() {
208 if (!pango_metrics_inited_
) {
209 pango_metrics_inited_
= true;
210 PangoFontDescription
* pango_desc
= PangoFontFromGfxFont(*this);
211 PangoFontMetrics
* pango_metrics
= GetPangoFontMetrics(pango_desc
);
213 underline_position_
=
214 pango_font_metrics_get_underline_position(pango_metrics
);
215 underline_position_
/= PANGO_SCALE
;
217 // todo(davemoore) Come up with a better solution.
218 // This is a hack, but without doing this the underlines
219 // we get end up fuzzy. So we align to the midpoint of a pixel.
220 underline_position_
/= 2;
222 underline_thickness_
=
223 pango_font_metrics_get_underline_thickness(pango_metrics
);
224 underline_thickness_
/= PANGO_SCALE
;
226 // First get the pango based width
228 pango_font_metrics_get_approximate_char_width(pango_metrics
);
229 pango_width
/= PANGO_SCALE
;
231 // Yes, this is how Microsoft recommends calculating the dialog unit
233 int text_width
= GetStringWidth(
234 L
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
235 double dialog_units
= (text_width
/ 26 + 1) / 2;
236 avg_width_
= std::min(pango_width
, dialog_units
);
237 pango_font_description_free(pango_desc
);
241 double Font::avg_width() const {
242 const_cast<Font
*>(this)->InitPangoMetrics();
246 double Font::underline_position() const {
247 const_cast<Font
*>(this)->InitPangoMetrics();
248 return underline_position_
;
251 double Font::underline_thickness() const {
252 const_cast<Font
*>(this)->InitPangoMetrics();
253 return underline_thickness_
;
256 int Font::GetExpectedTextWidth(int length
) const {
257 double char_width
= const_cast<Font
*>(this)->avg_width();
258 return round(static_cast<float>(length
) * char_width
);
261 int Font::style() const {
265 const std::wstring
& Font::FontName() const {
269 int Font::FontSize() {
273 NativeFont
Font::nativeFont() const {