OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ui/gfx/platform_font_pango.h" | |
6 | |
7 #include <pango/pango.h> | |
8 | |
9 #include <algorithm> | |
10 #include <string> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/strings/string_piece.h" | |
14 #include "base/strings/string_split.h" | |
15 #include "base/strings/utf_string_conversions.h" | |
16 #include "third_party/skia/include/core/SkPaint.h" | |
17 #include "third_party/skia/include/core/SkString.h" | |
18 #include "third_party/skia/include/core/SkTypeface.h" | |
19 #include "ui/gfx/canvas.h" | |
20 #include "ui/gfx/font.h" | |
21 #include "ui/gfx/font_list.h" | |
22 #include "ui/gfx/linux_font_delegate.h" | |
23 #include "ui/gfx/pango_util.h" | |
24 #include "ui/gfx/text_utils.h" | |
25 | |
26 namespace { | |
27 | |
28 // The font family name which is used when a user's application font for | |
29 // GNOME/KDE is a non-scalable one. The name should be listed in the | |
30 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp. | |
31 const char* kFallbackFontFamilyName = "sans"; | |
32 | |
33 // Creates a SkTypeface for the passed-in Font::FontStyle and family. If a | |
34 // fallback typeface is used instead of the requested family, |family| will be | |
35 // updated to contain the fallback's family name. | |
36 skia::RefPtr<SkTypeface> CreateSkTypeface(int style, std::string* family) { | |
37 DCHECK(family); | |
38 | |
39 int skia_style = SkTypeface::kNormal; | |
40 if (gfx::Font::BOLD & style) | |
41 skia_style |= SkTypeface::kBold; | |
42 if (gfx::Font::ITALIC & style) | |
43 skia_style |= SkTypeface::kItalic; | |
44 | |
45 skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(SkTypeface::CreateFromName( | |
46 family->c_str(), static_cast<SkTypeface::Style>(skia_style))); | |
47 if (!typeface) { | |
48 // A non-scalable font such as .pcf is specified. Fall back to a default | |
49 // scalable font. | |
50 typeface = skia::AdoptRef(SkTypeface::CreateFromName( | |
51 kFallbackFontFamilyName, static_cast<SkTypeface::Style>(skia_style))); | |
52 CHECK(typeface) << "Could not find any font: " << family << ", " | |
53 << kFallbackFontFamilyName; | |
54 *family = kFallbackFontFamilyName; | |
55 } | |
56 return typeface; | |
57 } | |
58 | |
59 } // namespace | |
60 | |
61 namespace gfx { | |
62 | |
63 // static | |
64 Font* PlatformFontPango::default_font_ = NULL; | |
65 | |
66 #if defined(OS_CHROMEOS) | |
67 // static | |
68 std::string* PlatformFontPango::default_font_description_ = NULL; | |
69 #endif | |
70 | |
71 //////////////////////////////////////////////////////////////////////////////// | |
72 // PlatformFontPango, public: | |
73 | |
74 PlatformFontPango::PlatformFontPango() { | |
75 if (!default_font_) { | |
76 scoped_ptr<ScopedPangoFontDescription> description; | |
77 #if defined(OS_CHROMEOS) | |
78 CHECK(default_font_description_); | |
79 description.reset( | |
80 new ScopedPangoFontDescription(*default_font_description_)); | |
81 #else | |
82 const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance(); | |
83 if (delegate) | |
84 description = delegate->GetDefaultPangoFontDescription(); | |
85 #endif | |
86 if (!description || !description->get()) | |
87 description.reset(new ScopedPangoFontDescription("sans 10")); | |
88 default_font_ = new Font(description->get()); | |
89 } | |
90 | |
91 InitFromPlatformFont( | |
92 static_cast<PlatformFontPango*>(default_font_->platform_font())); | |
93 } | |
94 | |
95 PlatformFontPango::PlatformFontPango(NativeFont native_font) { | |
96 FontRenderParamsQuery query(false); | |
97 base::SplitString(pango_font_description_get_family(native_font), ',', | |
98 &query.families); | |
99 | |
100 const int pango_size = | |
101 pango_font_description_get_size(native_font) / PANGO_SCALE; | |
102 if (pango_font_description_get_size_is_absolute(native_font)) | |
103 query.pixel_size = pango_size; | |
104 else | |
105 query.point_size = pango_size; | |
106 | |
107 query.style = gfx::Font::NORMAL; | |
108 // TODO(davemoore): Support weights other than bold? | |
109 if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) | |
110 query.style |= gfx::Font::BOLD; | |
111 // TODO(davemoore): What about PANGO_STYLE_OBLIQUE? | |
112 if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) | |
113 query.style |= gfx::Font::ITALIC; | |
114 | |
115 std::string font_family; | |
116 const FontRenderParams params = gfx::GetFontRenderParams(query, &font_family); | |
117 InitFromDetails(skia::RefPtr<SkTypeface>(), font_family, | |
118 gfx::GetPangoFontSizeInPixels(native_font), | |
119 query.style, params); | |
120 } | |
121 | |
122 PlatformFontPango::PlatformFontPango(const std::string& font_name, | |
123 int font_size_pixels) { | |
124 FontRenderParamsQuery query(false); | |
125 query.families.push_back(font_name); | |
126 query.pixel_size = font_size_pixels; | |
127 query.style = gfx::Font::NORMAL; | |
128 InitFromDetails(skia::RefPtr<SkTypeface>(), font_name, font_size_pixels, | |
129 query.style, gfx::GetFontRenderParams(query, NULL)); | |
130 } | |
131 | |
132 //////////////////////////////////////////////////////////////////////////////// | |
133 // PlatformFontPango, PlatformFont implementation: | |
134 | |
135 // static | |
136 void PlatformFontPango::ReloadDefaultFont() { | |
137 delete default_font_; | |
138 default_font_ = NULL; | |
139 } | |
140 | |
141 #if defined(OS_CHROMEOS) | |
142 // static | |
143 void PlatformFontPango::SetDefaultFontDescription( | |
144 const std::string& font_description) { | |
145 delete default_font_description_; | |
146 default_font_description_ = new std::string(font_description); | |
147 } | |
148 | |
149 #endif | |
150 | |
151 Font PlatformFontPango::DeriveFont(int size_delta, int style) const { | |
152 const int new_size = font_size_pixels_ + size_delta; | |
153 DCHECK_GT(new_size, 0); | |
154 | |
155 // If the style changed, we may need to load a new face. | |
156 std::string new_family = font_family_; | |
157 skia::RefPtr<SkTypeface> typeface = | |
158 (style == style_) ? typeface_ : CreateSkTypeface(style, &new_family); | |
159 | |
160 FontRenderParamsQuery query(false); | |
161 query.families.push_back(new_family); | |
162 query.pixel_size = new_size; | |
163 query.style = style; | |
164 | |
165 return Font(new PlatformFontPango(typeface, new_family, new_size, style, | |
166 gfx::GetFontRenderParams(query, NULL))); | |
167 } | |
168 | |
169 int PlatformFontPango::GetHeight() const { | |
170 return height_pixels_; | |
171 } | |
172 | |
173 int PlatformFontPango::GetBaseline() const { | |
174 return ascent_pixels_; | |
175 } | |
176 | |
177 int PlatformFontPango::GetCapHeight() const { | |
178 return cap_height_pixels_; | |
179 } | |
180 | |
181 int PlatformFontPango::GetExpectedTextWidth(int length) const { | |
182 double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth(); | |
183 return round(static_cast<float>(length) * char_width); | |
184 } | |
185 | |
186 int PlatformFontPango::GetStyle() const { | |
187 return style_; | |
188 } | |
189 | |
190 std::string PlatformFontPango::GetFontName() const { | |
191 return font_family_; | |
192 } | |
193 | |
194 std::string PlatformFontPango::GetActualFontNameForTesting() const { | |
195 SkString family_name; | |
196 typeface_->getFamilyName(&family_name); | |
197 return family_name.c_str(); | |
198 } | |
199 | |
200 int PlatformFontPango::GetFontSize() const { | |
201 return font_size_pixels_; | |
202 } | |
203 | |
204 const FontRenderParams& PlatformFontPango::GetFontRenderParams() const { | |
205 return font_render_params_; | |
206 } | |
207 | |
208 NativeFont PlatformFontPango::GetNativeFont() const { | |
209 PangoFontDescription* pfd = pango_font_description_new(); | |
210 pango_font_description_set_family(pfd, GetFontName().c_str()); | |
211 // Set the absolute size to avoid overflowing UI elements. | |
212 // pango_font_description_set_absolute_size() takes a size in Pango units. | |
213 // There are PANGO_SCALE Pango units in one device unit. Screen output | |
214 // devices use pixels as their device units. | |
215 pango_font_description_set_absolute_size( | |
216 pfd, font_size_pixels_ * PANGO_SCALE); | |
217 | |
218 switch (GetStyle()) { | |
219 case gfx::Font::NORMAL: | |
220 // Nothing to do, should already be PANGO_STYLE_NORMAL. | |
221 break; | |
222 case gfx::Font::BOLD: | |
223 pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD); | |
224 break; | |
225 case gfx::Font::ITALIC: | |
226 pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC); | |
227 break; | |
228 case gfx::Font::UNDERLINE: | |
229 // TODO(deanm): How to do underline? Where do we use it? Probably have | |
230 // to paint it ourselves, see pango_font_metrics_get_underline_position. | |
231 break; | |
232 } | |
233 | |
234 return pfd; | |
235 } | |
236 | |
237 //////////////////////////////////////////////////////////////////////////////// | |
238 // PlatformFontPango, private: | |
239 | |
240 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface, | |
241 const std::string& name, | |
242 int size_pixels, | |
243 int style, | |
244 const FontRenderParams& render_params) { | |
245 InitFromDetails(typeface, name, size_pixels, style, render_params); | |
246 } | |
247 | |
248 PlatformFontPango::~PlatformFontPango() {} | |
249 | |
250 void PlatformFontPango::InitFromDetails( | |
251 const skia::RefPtr<SkTypeface>& typeface, | |
252 const std::string& font_family, | |
253 int font_size_pixels, | |
254 int style, | |
255 const FontRenderParams& render_params) { | |
256 DCHECK_GT(font_size_pixels, 0); | |
257 | |
258 font_family_ = font_family; | |
259 typeface_ = typeface ? typeface : CreateSkTypeface(style, &font_family_); | |
260 | |
261 font_size_pixels_ = font_size_pixels; | |
262 style_ = style; | |
263 font_render_params_ = render_params; | |
264 | |
265 SkPaint paint; | |
266 SkPaint::FontMetrics metrics; | |
267 PaintSetup(&paint); | |
268 paint.getFontMetrics(&metrics); | |
269 ascent_pixels_ = SkScalarCeilToInt(-metrics.fAscent); | |
270 height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent); | |
271 cap_height_pixels_ = SkScalarCeilToInt(metrics.fCapHeight); | |
272 | |
273 pango_metrics_inited_ = false; | |
274 average_width_pixels_ = 0.0f; | |
275 } | |
276 | |
277 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) { | |
278 typeface_ = other->typeface_; | |
279 font_family_ = other->font_family_; | |
280 font_size_pixels_ = other->font_size_pixels_; | |
281 style_ = other->style_; | |
282 font_render_params_ = other->font_render_params_; | |
283 ascent_pixels_ = other->ascent_pixels_; | |
284 height_pixels_ = other->height_pixels_; | |
285 cap_height_pixels_ = other->cap_height_pixels_; | |
286 pango_metrics_inited_ = other->pango_metrics_inited_; | |
287 average_width_pixels_ = other->average_width_pixels_; | |
288 } | |
289 | |
290 void PlatformFontPango::PaintSetup(SkPaint* paint) const { | |
291 paint->setAntiAlias(false); | |
292 paint->setSubpixelText(false); | |
293 paint->setTextSize(font_size_pixels_); | |
294 paint->setTypeface(typeface_.get()); | |
295 paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold()); | |
296 paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ? | |
297 -SK_Scalar1/4 : 0); | |
298 } | |
299 | |
300 void PlatformFontPango::InitPangoMetrics() { | |
301 if (!pango_metrics_inited_) { | |
302 pango_metrics_inited_ = true; | |
303 ScopedPangoFontDescription pango_desc(GetNativeFont()); | |
304 PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get()); | |
305 | |
306 // First get the Pango-based width (converting from Pango units to pixels). | |
307 const double pango_width_pixels = | |
308 pango_font_metrics_get_approximate_char_width(pango_metrics) / | |
309 PANGO_SCALE; | |
310 | |
311 // Yes, this is how Microsoft recommends calculating the dialog unit | |
312 // conversions. | |
313 const int text_width_pixels = GetStringWidth( | |
314 base::ASCIIToUTF16( | |
315 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), | |
316 FontList(Font(this))); | |
317 const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2; | |
318 average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels); | |
319 } | |
320 } | |
321 | |
322 double PlatformFontPango::GetAverageWidth() const { | |
323 const_cast<PlatformFontPango*>(this)->InitPangoMetrics(); | |
324 return average_width_pixels_; | |
325 } | |
326 | |
327 //////////////////////////////////////////////////////////////////////////////// | |
328 // PlatformFont, public: | |
329 | |
330 // static | |
331 PlatformFont* PlatformFont::CreateDefault() { | |
332 return new PlatformFontPango; | |
333 } | |
334 | |
335 // static | |
336 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { | |
337 return new PlatformFontPango(native_font); | |
338 } | |
339 | |
340 // static | |
341 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, | |
342 int font_size) { | |
343 return new PlatformFontPango(font_name, font_size); | |
344 } | |
345 | |
346 } // namespace gfx | |
OLD | NEW |