OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "gfx/font.h" | |
6 | |
7 #include <gdk/gdk.h> | |
8 #include <map> | |
9 #include <pango/pango.h> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/string_piece.h" | |
13 #include "base/sys_string_conversions.h" | |
14 #include "gfx/canvas_skia.h" | |
15 #include "third_party/skia/include/core/SkTypeface.h" | |
16 #include "third_party/skia/include/core/SkPaint.h" | |
17 | |
18 namespace { | |
19 | |
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"; | |
24 | |
25 // Retrieves the pango metrics for a pango font description. Caches the metrics | |
26 // and never frees them. The metrics objects are relatively small and | |
27 // very expensive to look up. | |
28 static PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) { | |
29 static std::map<int, PangoFontMetrics*>* desc_to_metrics = NULL; | |
30 static PangoContext* context = NULL; | |
31 | |
32 if (!context) { | |
33 context = gdk_pango_context_get_for_screen(gdk_screen_get_default()); | |
34 pango_context_set_language(context, pango_language_get_default()); | |
35 } | |
36 | |
37 if (!desc_to_metrics) { | |
38 desc_to_metrics = new std::map<int, PangoFontMetrics*>(); | |
39 } | |
40 | |
41 int desc_hash = pango_font_description_hash(desc); | |
42 std::map<int, PangoFontMetrics*>::iterator i = | |
43 desc_to_metrics->find(desc_hash); | |
44 | |
45 if (i == desc_to_metrics->end()) { | |
46 PangoFontMetrics* metrics = pango_context_get_metrics(context, desc, NULL); | |
47 (*desc_to_metrics)[desc_hash] = metrics; | |
48 return metrics; | |
49 } else { | |
50 return i->second; | |
51 } | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 namespace gfx { | |
57 | |
58 Font::Font(const Font& other) { | |
59 CopyFont(other); | |
60 } | |
61 | |
62 Font& Font::operator=(const Font& other) { | |
63 CopyFont(other); | |
64 return *this; | |
65 } | |
66 | |
67 Font::Font(SkTypeface* tf, const std::wstring& font_family, int font_size, | |
68 int style) | |
69 : typeface_helper_(new SkAutoUnref(tf)), | |
70 typeface_(tf), | |
71 font_family_(font_family), | |
72 font_size_(font_size), | |
73 style_(style), | |
74 pango_metrics_inited_(false), | |
75 avg_width_(0.0), | |
76 underline_position_(0.0), | |
77 underline_thickness_(0.0) { | |
78 tf->ref(); | |
79 calculateMetrics(); | |
80 } | |
81 | |
82 void Font::calculateMetrics() { | |
83 SkPaint paint; | |
84 SkPaint::FontMetrics metrics; | |
85 PaintSetup(&paint); | |
86 paint.getFontMetrics(&metrics); | |
87 | |
88 ascent_ = SkScalarCeil(-metrics.fAscent); | |
89 height_ = ascent_ + SkScalarCeil(metrics.fDescent); | |
90 | |
91 } | |
92 | |
93 void Font::CopyFont(const Font& other) { | |
94 typeface_helper_.reset(new SkAutoUnref(other.typeface_)); | |
95 typeface_ = other.typeface_; | |
96 typeface_->ref(); | |
97 font_family_ = other.font_family_; | |
98 font_size_ = other.font_size_; | |
99 style_ = other.style_; | |
100 height_ = other.height_; | |
101 ascent_ = other.ascent_; | |
102 pango_metrics_inited_ = other.pango_metrics_inited_; | |
103 avg_width_ = other.avg_width_; | |
104 underline_position_ = other.underline_position_; | |
105 underline_thickness_ = other.underline_thickness_; | |
106 } | |
107 | |
108 int Font::height() const { | |
109 return height_; | |
110 } | |
111 | |
112 int Font::baseline() const { | |
113 return ascent_; | |
114 } | |
115 | |
116 int Font::ave_char_width() const { | |
117 return SkScalarRound(avg_width()); | |
118 } | |
119 | |
120 Font Font::CreateFont(const std::wstring& font_family, int font_size) { | |
121 DCHECK_GT(font_size, 0); | |
122 std::wstring fallback; | |
123 | |
124 SkTypeface* tf = SkTypeface::CreateFromName( | |
125 base::SysWideToUTF8(font_family).c_str(), SkTypeface::kNormal); | |
126 if (!tf) { | |
127 // A non-scalable font such as .pcf is specified. Falls back to a default | |
128 // scalable font. | |
129 tf = SkTypeface::CreateFromName( | |
130 kFallbackFontFamilyName, SkTypeface::kNormal); | |
131 CHECK(tf) << "Could not find any font: " | |
132 << base::SysWideToUTF8(font_family) | |
133 << ", " << kFallbackFontFamilyName; | |
134 fallback = base::SysUTF8ToWide(kFallbackFontFamilyName); | |
135 } | |
136 SkAutoUnref tf_helper(tf); | |
137 | |
138 return Font( | |
139 tf, fallback.empty() ? font_family : fallback, font_size, NORMAL); | |
140 } | |
141 | |
142 Font Font::DeriveFont(int size_delta, int style) const { | |
143 // If the delta is negative, if must not push the size below 1 | |
144 if (size_delta < 0) { | |
145 DCHECK_LT(-size_delta, font_size_); | |
146 } | |
147 | |
148 if (style == style_) { | |
149 // Fast path, we just use the same typeface at a different size | |
150 return Font(typeface_, font_family_, font_size_ + size_delta, style_); | |
151 } | |
152 | |
153 // If the style has changed we may need to load a new face | |
154 int skstyle = SkTypeface::kNormal; | |
155 if (BOLD & style) | |
156 skstyle |= SkTypeface::kBold; | |
157 if (ITALIC & style) | |
158 skstyle |= SkTypeface::kItalic; | |
159 | |
160 SkTypeface* tf = SkTypeface::CreateFromName( | |
161 base::SysWideToUTF8(font_family_).c_str(), | |
162 static_cast<SkTypeface::Style>(skstyle)); | |
163 SkAutoUnref tf_helper(tf); | |
164 | |
165 return Font(tf, font_family_, font_size_ + size_delta, style); | |
166 } | |
167 | |
168 void Font::PaintSetup(SkPaint* paint) const { | |
169 paint->setAntiAlias(false); | |
170 paint->setSubpixelText(false); | |
171 paint->setTextSize(SkFloatToScalar(font_size_ * Font::GetPangoScaleFactor())); | |
172 paint->setTypeface(typeface_); | |
173 paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold()); | |
174 paint->setTextSkewX((ITALIC & style_) && !typeface_->isItalic() ? | |
175 -SK_Scalar1/4 : 0); | |
176 } | |
177 | |
178 int Font::GetStringWidth(const std::wstring& text) const { | |
179 int width = 0, height = 0; | |
180 CanvasSkia::SizeStringInt(text, *this, &width, &height, | |
181 gfx::Canvas::NO_ELLIPSIS); | |
182 return width; | |
183 } | |
184 | |
185 void Font::InitPangoMetrics() { | |
186 if (!pango_metrics_inited_) { | |
187 pango_metrics_inited_ = true; | |
188 PangoFontDescription* pango_desc = PangoFontFromGfxFont(*this); | |
189 PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc); | |
190 | |
191 underline_position_ = | |
192 pango_font_metrics_get_underline_position(pango_metrics); | |
193 underline_position_ /= PANGO_SCALE; | |
194 | |
195 // todo(davemoore) Come up with a better solution. | |
196 // This is a hack, but without doing this the underlines | |
197 // we get end up fuzzy. So we align to the midpoint of a pixel. | |
198 underline_position_ /= 2; | |
199 | |
200 underline_thickness_ = | |
201 pango_font_metrics_get_underline_thickness(pango_metrics); | |
202 underline_thickness_ /= PANGO_SCALE; | |
203 | |
204 // First get the pango based width | |
205 double pango_width = | |
206 pango_font_metrics_get_approximate_char_width(pango_metrics); | |
207 pango_width /= PANGO_SCALE; | |
208 | |
209 // Yes, this is how Microsoft recommends calculating the dialog unit | |
210 // conversions. | |
211 int text_width = GetStringWidth( | |
212 L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); | |
213 double dialog_units = (text_width / 26 + 1) / 2; | |
214 avg_width_ = std::min(pango_width, dialog_units); | |
215 pango_font_description_free(pango_desc); | |
216 } | |
217 } | |
218 | |
219 double Font::avg_width() const { | |
220 const_cast<Font*>(this)->InitPangoMetrics(); | |
221 return avg_width_; | |
222 } | |
223 | |
224 double Font::underline_position() const { | |
225 const_cast<Font*>(this)->InitPangoMetrics(); | |
226 return underline_position_; | |
227 } | |
228 | |
229 double Font::underline_thickness() const { | |
230 const_cast<Font*>(this)->InitPangoMetrics(); | |
231 return underline_thickness_; | |
232 } | |
233 | |
234 int Font::GetExpectedTextWidth(int length) const { | |
235 double char_width = const_cast<Font*>(this)->avg_width(); | |
236 return round(static_cast<float>(length) * char_width); | |
237 } | |
238 | |
239 int Font::style() const { | |
240 return style_; | |
241 } | |
242 | |
243 const std::wstring& Font::FontName() const { | |
244 return font_family_; | |
245 } | |
246 | |
247 int Font::FontSize() { | |
248 return font_size_; | |
249 } | |
250 | |
251 NativeFont Font::nativeFont() const { | |
252 return typeface_; | |
253 } | |
254 | |
255 } // namespace gfx | |
OLD | NEW |