OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "app/gfx/font.h" | |
6 | |
7 #include <gdk/gdk.h> | |
8 #include <map> | |
9 #include <pango/pango.h> | |
10 | |
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" | |
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 // 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; | |
43 } | |
44 return scale_factor; | |
45 } | |
46 | |
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; | |
53 | |
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()); | |
57 } | |
58 | |
59 if (!desc_to_metrics) { | |
60 desc_to_metrics = new std::map<int, PangoFontMetrics*>(); | |
61 } | |
62 | |
63 int desc_hash = pango_font_description_hash(desc); | |
64 std::map<int, PangoFontMetrics*>::iterator i = | |
65 desc_to_metrics->find(desc_hash); | |
66 | |
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; | |
73 } | |
74 } | |
75 | |
76 } // namespace | |
77 | |
78 namespace gfx { | |
79 | |
80 Font::Font(const Font& other) { | |
81 CopyFont(other); | |
82 } | |
83 | |
84 Font& Font::operator=(const Font& other) { | |
85 CopyFont(other); | |
86 return *this; | |
87 } | |
88 | |
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(); | |
102 } | |
103 | |
104 void Font::calculateMetrics() { | |
105 SkPaint paint; | |
106 SkPaint::FontMetrics metrics; | |
107 PaintSetup(&paint); | |
108 paint.getFontMetrics(&metrics); | |
109 | |
110 ascent_ = SkScalarCeil(-metrics.fAscent); | |
111 height_ = ascent_ + SkScalarCeil(metrics.fDescent); | |
112 | |
113 } | |
114 | |
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_; | |
128 } | |
129 | |
130 int Font::height() const { | |
131 return height_; | |
132 } | |
133 | |
134 int Font::baseline() const { | |
135 return ascent_; | |
136 } | |
137 | |
138 int Font::ave_char_width() const { | |
139 return SkScalarRound(avg_width()); | |
140 } | |
141 | |
142 Font Font::CreateFont(const std::wstring& font_family, int font_size) { | |
143 DCHECK_GT(font_size, 0); | |
144 std::wstring fallback; | |
145 | |
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); | |
157 } | |
158 SkAutoUnref tf_helper(tf); | |
159 | |
160 return Font( | |
161 tf, fallback.empty() ? font_family : fallback, font_size, NORMAL); | |
162 } | |
163 | |
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_); | |
168 } | |
169 | |
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_); | |
173 } | |
174 | |
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; | |
181 | |
182 SkTypeface* tf = SkTypeface::CreateFromName( | |
183 base::SysWideToUTF8(font_family_).c_str(), | |
184 static_cast<SkTypeface::Style>(skstyle)); | |
185 SkAutoUnref tf_helper(tf); | |
186 | |
187 return Font(tf, font_family_, font_size_ + size_delta, style); | |
188 } | |
189 | |
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); | |
198 } | |
199 | |
200 int Font::GetStringWidth(const std::wstring& text) const { | |
201 int width = 0, height = 0; | |
202 | |
203 Canvas::SizeStringInt(text, *this, &width, &height, 0); | |
204 return width; | |
205 } | |
206 | |
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); | |
212 | |
213 underline_position_ = | |
214 pango_font_metrics_get_underline_position(pango_metrics); | |
215 underline_position_ /= PANGO_SCALE; | |
216 | |
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; | |
221 | |
222 underline_thickness_ = | |
223 pango_font_metrics_get_underline_thickness(pango_metrics); | |
224 underline_thickness_ /= PANGO_SCALE; | |
225 | |
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; | |
230 | |
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); | |
238 } | |
239 } | |
240 | |
241 double Font::avg_width() const { | |
242 const_cast<Font*>(this)->InitPangoMetrics(); | |
243 return avg_width_; | |
244 } | |
245 | |
246 double Font::underline_position() const { | |
247 const_cast<Font*>(this)->InitPangoMetrics(); | |
248 return underline_position_; | |
249 } | |
250 | |
251 double Font::underline_thickness() const { | |
252 const_cast<Font*>(this)->InitPangoMetrics(); | |
253 return underline_thickness_; | |
254 } | |
255 | |
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); | |
259 } | |
260 | |
261 int Font::style() const { | |
262 return style_; | |
263 } | |
264 | |
265 const std::wstring& Font::FontName() const { | |
266 return font_family_; | |
267 } | |
268 | |
269 int Font::FontSize() { | |
270 return font_size_; | |
271 } | |
272 | |
273 NativeFont Font::nativeFont() const { | |
274 return typeface_; | |
275 } | |
276 | |
277 } // namespace gfx | |
OLD | NEW |