WebKit Roll 56306:56312.
[chromium-blink-merge.git] / app / gfx / font_skia.cc
blobeee1238c7263a4edb251fbce7030849c1cada895
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"
7 #include <gdk/gdk.h>
8 #include <map>
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"
18 namespace {
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
30 // English and Thia.
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)
39 scale_factor = 1;
40 else
41 scale_factor /= 72.0;
42 determined_scale = true;
44 return scale_factor;
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;
54 if (!context) {
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;
70 return metrics;
71 } else {
72 return i->second;
76 } // namespace
78 namespace gfx {
80 Font::Font(const Font& other) {
81 CopyFont(other);
84 Font& Font::operator=(const Font& other) {
85 CopyFont(other);
86 return *this;
89 Font::Font(SkTypeface* tf, const std::wstring& font_family, int font_size,
90 int style)
91 : typeface_helper_(new SkAutoUnref(tf)),
92 typeface_(tf),
93 font_family_(font_family),
94 font_size_(font_size),
95 style_(style),
96 pango_metrics_inited_(false),
97 avg_width_(0.0),
98 underline_position_(0.0),
99 underline_thickness_(0.0) {
100 tf->ref();
101 calculateMetrics();
104 void Font::calculateMetrics() {
105 SkPaint paint;
106 SkPaint::FontMetrics metrics;
107 PaintSetup(&paint);
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_;
118 typeface_->ref();
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 {
131 return height_;
134 int Font::baseline() const {
135 return ascent_;
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);
148 if (!tf) {
149 // A non-scalable font such as .pcf is specified. Falls back to a default
150 // scalable font.
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);
160 return Font(
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;
177 if (BOLD & style)
178 skstyle |= SkTypeface::kBold;
179 if (ITALIC & style)
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() ?
197 -SK_Scalar1/4 : 0);
200 int Font::GetStringWidth(const std::wstring& text) const {
201 int width = 0, height = 0;
203 Canvas::SizeStringInt(text, *this, &width, &height, 0);
204 return width;
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
227 double pango_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
232 // conversions.
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();
243 return avg_width_;
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 {
262 return style_;
265 const std::wstring& Font::FontName() const {
266 return font_family_;
269 int Font::FontSize() {
270 return font_size_;
273 NativeFont Font::nativeFont() const {
274 return typeface_;
277 } // namespace gfx