| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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_gtk.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <fontconfig/fontconfig.h> | |
| 9 #include <map> | |
| 10 #include <pango/pango.h> | |
| 11 #include <string> | |
| 12 | |
| 13 #include "base/logging.h" | |
| 14 #include "base/string_piece.h" | |
| 15 #include "base/utf_string_conversions.h" | |
| 16 #include "third_party/skia/include/core/SkTypeface.h" | |
| 17 #include "third_party/skia/include/core/SkPaint.h" | |
| 18 #include "ui/gfx/canvas_skia.h" | |
| 19 #include "ui/gfx/font.h" | |
| 20 #include "ui/gfx/linux_util.h" | |
| 21 | |
| 22 #if !defined(USE_WAYLAND) | |
| 23 #include <gdk/gdk.h> | |
| 24 #include <gtk/gtk.h> | |
| 25 #endif | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // The font family name which is used when a user's application font for | |
| 30 // GNOME/KDE is a non-scalable one. The name should be listed in the | |
| 31 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp. | |
| 32 const char* kFallbackFontFamilyName = "sans"; | |
| 33 | |
| 34 // Returns the number of pixels in a point. | |
| 35 // - multiply a point size by this to get pixels ("device units") | |
| 36 // - divide a pixel size by this to get points | |
| 37 float GetPixelsInPoint() { | |
| 38 static float pixels_in_point = 1.0; | |
| 39 static bool determined_value = false; | |
| 40 | |
| 41 if (!determined_value) { | |
| 42 // http://goo.gl/UIh5m: "This is a scale factor between points specified in | |
| 43 // a PangoFontDescription and Cairo units. The default value is 96, meaning | |
| 44 // that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3)." | |
| 45 double pango_dpi = gfx::GetPangoResolution(); | |
| 46 if (pango_dpi <= 0) | |
| 47 pango_dpi = 96.0; | |
| 48 pixels_in_point = pango_dpi / 72.0; // 72 points in an inch | |
| 49 determined_value = true; | |
| 50 } | |
| 51 | |
| 52 return pixels_in_point; | |
| 53 } | |
| 54 | |
| 55 // Retrieves the pango metrics for a pango font description. Caches the metrics | |
| 56 // and never frees them. The metrics objects are relatively small and | |
| 57 // very expensive to look up. | |
| 58 PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) { | |
| 59 static std::map<int, PangoFontMetrics*>* desc_to_metrics = NULL; | |
| 60 static PangoContext* context = NULL; | |
| 61 | |
| 62 if (!context) { | |
| 63 context = gfx::GetPangoContext(); | |
| 64 pango_context_set_language(context, pango_language_get_default()); | |
| 65 } | |
| 66 | |
| 67 if (!desc_to_metrics) { | |
| 68 desc_to_metrics = new std::map<int, PangoFontMetrics*>(); | |
| 69 } | |
| 70 | |
| 71 int desc_hash = pango_font_description_hash(desc); | |
| 72 std::map<int, PangoFontMetrics*>::iterator i = | |
| 73 desc_to_metrics->find(desc_hash); | |
| 74 | |
| 75 if (i == desc_to_metrics->end()) { | |
| 76 PangoFontMetrics* metrics = pango_context_get_metrics(context, desc, NULL); | |
| 77 (*desc_to_metrics)[desc_hash] = metrics; | |
| 78 return metrics; | |
| 79 } else { | |
| 80 return i->second; | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 // Find the best match font for |family_name| in the same way as Skia | |
| 85 // to make sure CreateFont() successfully creates a default font. In | |
| 86 // Skia, it only checks the best match font. If it failed to find | |
| 87 // one, SkTypeface will be NULL for that font family. It eventually | |
| 88 // causes a segfault. For example, family_name = "Sans" and system | |
| 89 // may have various fonts. The first font family in FcPattern will be | |
| 90 // "DejaVu Sans" but a font family returned by FcFontMatch will be "VL | |
| 91 // PGothic". In this case, SkTypeface for "Sans" returns NULL even if | |
| 92 // the system has a font for "Sans" font family. See FontMatch() in | |
| 93 // skia/ports/SkFontHost_fontconfig.cpp for more detail. | |
| 94 string16 FindBestMatchFontFamilyName(const char* family_name) { | |
| 95 FcPattern* pattern = FcPatternCreate(); | |
| 96 FcValue fcvalue; | |
| 97 fcvalue.type = FcTypeString; | |
| 98 char* family_name_copy = strdup(family_name); | |
| 99 fcvalue.u.s = reinterpret_cast<FcChar8*>(family_name_copy); | |
| 100 FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0); | |
| 101 FcConfigSubstitute(0, pattern, FcMatchPattern); | |
| 102 FcDefaultSubstitute(pattern); | |
| 103 FcResult result; | |
| 104 FcPattern* match = FcFontMatch(0, pattern, &result); | |
| 105 DCHECK(match) << "Could not find font: " << family_name; | |
| 106 FcChar8* match_family; | |
| 107 FcPatternGetString(match, FC_FAMILY, 0, &match_family); | |
| 108 | |
| 109 string16 font_family = UTF8ToUTF16(reinterpret_cast<char*>(match_family)); | |
| 110 FcPatternDestroy(match); | |
| 111 FcPatternDestroy(pattern); | |
| 112 free(family_name_copy); | |
| 113 return font_family; | |
| 114 } | |
| 115 | |
| 116 std::string GetDefaultFont() { | |
| 117 #if defined(USE_WAYLAND) || defined(USE_AURA) | |
| 118 return "sans 10"; | |
| 119 #else | |
| 120 GtkSettings* settings = gtk_settings_get_default(); | |
| 121 | |
| 122 gchar* font_name = NULL; | |
| 123 g_object_get(settings, "gtk-font-name", &font_name, NULL); | |
| 124 | |
| 125 // Temporary CHECK for helping track down | |
| 126 // http://code.google.com/p/chromium/issues/detail?id=12530 | |
| 127 CHECK(font_name) << " Unable to get gtk-font-name for default font."; | |
| 128 | |
| 129 std::string default_font = std::string(font_name); | |
| 130 g_free(font_name); | |
| 131 return default_font; | |
| 132 #endif | |
| 133 } | |
| 134 | |
| 135 } // namespace | |
| 136 | |
| 137 namespace gfx { | |
| 138 | |
| 139 Font* PlatformFontGtk::default_font_ = NULL; | |
| 140 | |
| 141 //////////////////////////////////////////////////////////////////////////////// | |
| 142 // PlatformFontGtk, public: | |
| 143 | |
| 144 PlatformFontGtk::PlatformFontGtk() { | |
| 145 if (default_font_ == NULL) { | |
| 146 std::string font_name = GetDefaultFont(); | |
| 147 | |
| 148 PangoFontDescription* desc = | |
| 149 pango_font_description_from_string(font_name.c_str()); | |
| 150 default_font_ = new Font(desc); | |
| 151 pango_font_description_free(desc); | |
| 152 | |
| 153 DCHECK(default_font_); | |
| 154 } | |
| 155 | |
| 156 InitFromPlatformFont( | |
| 157 static_cast<PlatformFontGtk*>(default_font_->platform_font())); | |
| 158 } | |
| 159 | |
| 160 PlatformFontGtk::PlatformFontGtk(const Font& other) { | |
| 161 InitFromPlatformFont( | |
| 162 static_cast<PlatformFontGtk*>(other.platform_font())); | |
| 163 } | |
| 164 | |
| 165 PlatformFontGtk::PlatformFontGtk(NativeFont native_font) { | |
| 166 const char* family_name = pango_font_description_get_family(native_font); | |
| 167 | |
| 168 gint size_in_pixels = 0; | |
| 169 if (pango_font_description_get_size_is_absolute(native_font)) { | |
| 170 // If the size is absolute, then it's in Pango units rather than points. | |
| 171 // There are PANGO_SCALE Pango units in a device unit (pixel). | |
| 172 size_in_pixels = pango_font_description_get_size(native_font) / PANGO_SCALE; | |
| 173 } else { | |
| 174 // Otherwise, we need to convert from points. | |
| 175 size_in_pixels = | |
| 176 pango_font_description_get_size(native_font) * GetPixelsInPoint() / | |
| 177 PANGO_SCALE; | |
| 178 } | |
| 179 | |
| 180 // Find best match font for |family_name| to make sure we can get | |
| 181 // a SkTypeface for the default font. | |
| 182 // TODO(agl): remove this. | |
| 183 string16 font_family = FindBestMatchFontFamilyName(family_name); | |
| 184 | |
| 185 InitWithNameAndSize(font_family, size_in_pixels); | |
| 186 int style = 0; | |
| 187 if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) { | |
| 188 // TODO(davemoore) What should we do about other weights? We currently | |
| 189 // only support BOLD. | |
| 190 style |= gfx::Font::BOLD; | |
| 191 } | |
| 192 if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) { | |
| 193 // TODO(davemoore) What about PANGO_STYLE_OBLIQUE? | |
| 194 style |= gfx::Font::ITALIC; | |
| 195 } | |
| 196 if (style != 0) | |
| 197 style_ = style; | |
| 198 } | |
| 199 | |
| 200 PlatformFontGtk::PlatformFontGtk(const string16& font_name, | |
| 201 int font_size) { | |
| 202 InitWithNameAndSize(font_name, font_size); | |
| 203 } | |
| 204 | |
| 205 double PlatformFontGtk::underline_position() const { | |
| 206 const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); | |
| 207 return underline_position_pixels_; | |
| 208 } | |
| 209 | |
| 210 double PlatformFontGtk::underline_thickness() const { | |
| 211 const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); | |
| 212 return underline_thickness_pixels_; | |
| 213 } | |
| 214 | |
| 215 //////////////////////////////////////////////////////////////////////////////// | |
| 216 // PlatformFontGtk, PlatformFont implementation: | |
| 217 | |
| 218 // static | |
| 219 void PlatformFontGtk::ReloadDefaultFont() { | |
| 220 delete default_font_; | |
| 221 default_font_ = NULL; | |
| 222 } | |
| 223 | |
| 224 Font PlatformFontGtk::DeriveFont(int size_delta, int style) const { | |
| 225 // If the delta is negative, if must not push the size below 1 | |
| 226 if (size_delta < 0) | |
| 227 DCHECK_LT(-size_delta, font_size_pixels_); | |
| 228 | |
| 229 if (style == style_) { | |
| 230 // Fast path, we just use the same typeface at a different size | |
| 231 return Font(new PlatformFontGtk(typeface_, | |
| 232 font_family_, | |
| 233 font_size_pixels_ + size_delta, | |
| 234 style_)); | |
| 235 } | |
| 236 | |
| 237 // If the style has changed we may need to load a new face | |
| 238 int skstyle = SkTypeface::kNormal; | |
| 239 if (gfx::Font::BOLD & style) | |
| 240 skstyle |= SkTypeface::kBold; | |
| 241 if (gfx::Font::ITALIC & style) | |
| 242 skstyle |= SkTypeface::kItalic; | |
| 243 | |
| 244 SkTypeface* typeface = SkTypeface::CreateFromName( | |
| 245 UTF16ToUTF8(font_family_).c_str(), | |
| 246 static_cast<SkTypeface::Style>(skstyle)); | |
| 247 SkAutoUnref tf_helper(typeface); | |
| 248 | |
| 249 return Font(new PlatformFontGtk(typeface, | |
| 250 font_family_, | |
| 251 font_size_pixels_ + size_delta, | |
| 252 style)); | |
| 253 } | |
| 254 | |
| 255 int PlatformFontGtk::GetHeight() const { | |
| 256 return height_pixels_; | |
| 257 } | |
| 258 | |
| 259 int PlatformFontGtk::GetBaseline() const { | |
| 260 return ascent_pixels_; | |
| 261 } | |
| 262 | |
| 263 int PlatformFontGtk::GetAverageCharacterWidth() const { | |
| 264 return SkScalarRound(average_width_pixels_); | |
| 265 } | |
| 266 | |
| 267 int PlatformFontGtk::GetStringWidth(const string16& text) const { | |
| 268 int width = 0, height = 0; | |
| 269 CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontGtk*>(this)), | |
| 270 &width, &height, gfx::Canvas::NO_ELLIPSIS); | |
| 271 return width; | |
| 272 } | |
| 273 | |
| 274 int PlatformFontGtk::GetExpectedTextWidth(int length) const { | |
| 275 double char_width = const_cast<PlatformFontGtk*>(this)->GetAverageWidth(); | |
| 276 return round(static_cast<float>(length) * char_width); | |
| 277 } | |
| 278 | |
| 279 int PlatformFontGtk::GetStyle() const { | |
| 280 return style_; | |
| 281 } | |
| 282 | |
| 283 string16 PlatformFontGtk::GetFontName() const { | |
| 284 return font_family_; | |
| 285 } | |
| 286 | |
| 287 int PlatformFontGtk::GetFontSize() const { | |
| 288 return font_size_pixels_; | |
| 289 } | |
| 290 | |
| 291 NativeFont PlatformFontGtk::GetNativeFont() const { | |
| 292 PangoFontDescription* pfd = pango_font_description_new(); | |
| 293 pango_font_description_set_family(pfd, UTF16ToUTF8(GetFontName()).c_str()); | |
| 294 // Set the absolute size to avoid overflowing UI elements. | |
| 295 // pango_font_description_set_absolute_size() takes a size in Pango units. | |
| 296 // There are PANGO_SCALE Pango units in one device unit. Screen output | |
| 297 // devices use pixels as their device units. | |
| 298 pango_font_description_set_absolute_size( | |
| 299 pfd, font_size_pixels_ * PANGO_SCALE); | |
| 300 | |
| 301 switch (GetStyle()) { | |
| 302 case gfx::Font::NORMAL: | |
| 303 // Nothing to do, should already be PANGO_STYLE_NORMAL. | |
| 304 break; | |
| 305 case gfx::Font::BOLD: | |
| 306 pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD); | |
| 307 break; | |
| 308 case gfx::Font::ITALIC: | |
| 309 pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC); | |
| 310 break; | |
| 311 case gfx::Font::UNDERLINED: | |
| 312 // TODO(deanm): How to do underlined? Where do we use it? Probably have | |
| 313 // to paint it ourselves, see pango_font_metrics_get_underline_position. | |
| 314 break; | |
| 315 } | |
| 316 | |
| 317 return pfd; | |
| 318 } | |
| 319 | |
| 320 //////////////////////////////////////////////////////////////////////////////// | |
| 321 // PlatformFontGtk, private: | |
| 322 | |
| 323 PlatformFontGtk::PlatformFontGtk(SkTypeface* typeface, | |
| 324 const string16& name, | |
| 325 int size, | |
| 326 int style) { | |
| 327 InitWithTypefaceNameSizeAndStyle(typeface, name, size, style); | |
| 328 } | |
| 329 | |
| 330 PlatformFontGtk::~PlatformFontGtk() {} | |
| 331 | |
| 332 void PlatformFontGtk::InitWithNameAndSize(const string16& font_name, | |
| 333 int font_size) { | |
| 334 DCHECK_GT(font_size, 0); | |
| 335 string16 fallback; | |
| 336 | |
| 337 SkTypeface* typeface = SkTypeface::CreateFromName( | |
| 338 UTF16ToUTF8(font_name).c_str(), SkTypeface::kNormal); | |
| 339 if (!typeface) { | |
| 340 // A non-scalable font such as .pcf is specified. Falls back to a default | |
| 341 // scalable font. | |
| 342 typeface = SkTypeface::CreateFromName( | |
| 343 kFallbackFontFamilyName, SkTypeface::kNormal); | |
| 344 CHECK(typeface) << "Could not find any font: " | |
| 345 << UTF16ToUTF8(font_name) | |
| 346 << ", " << kFallbackFontFamilyName; | |
| 347 fallback = UTF8ToUTF16(kFallbackFontFamilyName); | |
| 348 } | |
| 349 SkAutoUnref typeface_helper(typeface); | |
| 350 | |
| 351 InitWithTypefaceNameSizeAndStyle(typeface, | |
| 352 fallback.empty() ? font_name : fallback, | |
| 353 font_size, | |
| 354 gfx::Font::NORMAL); | |
| 355 } | |
| 356 | |
| 357 void PlatformFontGtk::InitWithTypefaceNameSizeAndStyle( | |
| 358 SkTypeface* typeface, | |
| 359 const string16& font_family, | |
| 360 int font_size, | |
| 361 int style) { | |
| 362 typeface_helper_.reset(new SkAutoUnref(typeface)); | |
| 363 typeface_ = typeface; | |
| 364 typeface_->ref(); | |
| 365 font_family_ = font_family; | |
| 366 font_size_pixels_ = font_size; | |
| 367 style_ = style; | |
| 368 pango_metrics_inited_ = false; | |
| 369 average_width_pixels_ = 0.0f; | |
| 370 underline_position_pixels_ = 0.0f; | |
| 371 underline_thickness_pixels_ = 0.0f; | |
| 372 | |
| 373 SkPaint paint; | |
| 374 SkPaint::FontMetrics metrics; | |
| 375 PaintSetup(&paint); | |
| 376 paint.getFontMetrics(&metrics); | |
| 377 | |
| 378 ascent_pixels_ = SkScalarCeil(-metrics.fAscent); | |
| 379 height_pixels_ = ascent_pixels_ + SkScalarCeil(metrics.fDescent); | |
| 380 } | |
| 381 | |
| 382 void PlatformFontGtk::InitFromPlatformFont(const PlatformFontGtk* other) { | |
| 383 typeface_helper_.reset(new SkAutoUnref(other->typeface_)); | |
| 384 typeface_ = other->typeface_; | |
| 385 typeface_->ref(); | |
| 386 font_family_ = other->font_family_; | |
| 387 font_size_pixels_ = other->font_size_pixels_; | |
| 388 style_ = other->style_; | |
| 389 height_pixels_ = other->height_pixels_; | |
| 390 ascent_pixels_ = other->ascent_pixels_; | |
| 391 pango_metrics_inited_ = other->pango_metrics_inited_; | |
| 392 average_width_pixels_ = other->average_width_pixels_; | |
| 393 underline_position_pixels_ = other->underline_position_pixels_; | |
| 394 underline_thickness_pixels_ = other->underline_thickness_pixels_; | |
| 395 } | |
| 396 | |
| 397 void PlatformFontGtk::PaintSetup(SkPaint* paint) const { | |
| 398 paint->setAntiAlias(false); | |
| 399 paint->setSubpixelText(false); | |
| 400 paint->setTextSize(font_size_pixels_); | |
| 401 paint->setTypeface(typeface_); | |
| 402 paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold()); | |
| 403 paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ? | |
| 404 -SK_Scalar1/4 : 0); | |
| 405 } | |
| 406 | |
| 407 void PlatformFontGtk::InitPangoMetrics() { | |
| 408 if (!pango_metrics_inited_) { | |
| 409 pango_metrics_inited_ = true; | |
| 410 PangoFontDescription* pango_desc = GetNativeFont(); | |
| 411 PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc); | |
| 412 | |
| 413 underline_position_pixels_ = | |
| 414 pango_font_metrics_get_underline_position(pango_metrics) / | |
| 415 PANGO_SCALE; | |
| 416 | |
| 417 // TODO(davemoore): Come up with a better solution. | |
| 418 // This is a hack, but without doing this the underlines | |
| 419 // we get end up fuzzy. So we align to the midpoint of a pixel. | |
| 420 underline_position_pixels_ /= 2; | |
| 421 | |
| 422 underline_thickness_pixels_ = | |
| 423 pango_font_metrics_get_underline_thickness(pango_metrics) / | |
| 424 PANGO_SCALE; | |
| 425 | |
| 426 // First get the Pango-based width (converting from Pango units to pixels). | |
| 427 double pango_width_pixels = | |
| 428 pango_font_metrics_get_approximate_char_width(pango_metrics) / | |
| 429 PANGO_SCALE; | |
| 430 | |
| 431 // Yes, this is how Microsoft recommends calculating the dialog unit | |
| 432 // conversions. | |
| 433 int text_width_pixels = GetStringWidth( | |
| 434 ASCIIToUTF16("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); | |
| 435 double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2; | |
| 436 average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels); | |
| 437 pango_font_description_free(pango_desc); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 | |
| 442 double PlatformFontGtk::GetAverageWidth() const { | |
| 443 const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); | |
| 444 return average_width_pixels_; | |
| 445 } | |
| 446 | |
| 447 //////////////////////////////////////////////////////////////////////////////// | |
| 448 // PlatformFont, public: | |
| 449 | |
| 450 // static | |
| 451 PlatformFont* PlatformFont::CreateDefault() { | |
| 452 return new PlatformFontGtk; | |
| 453 } | |
| 454 | |
| 455 // static | |
| 456 PlatformFont* PlatformFont::CreateFromFont(const Font& other) { | |
| 457 return new PlatformFontGtk(other); | |
| 458 } | |
| 459 | |
| 460 // static | |
| 461 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { | |
| 462 return new PlatformFontGtk(native_font); | |
| 463 } | |
| 464 | |
| 465 // static | |
| 466 PlatformFont* PlatformFont::CreateFromNameAndSize(const string16& font_name, | |
| 467 int font_size) { | |
| 468 return new PlatformFontGtk(font_name, font_size); | |
| 469 } | |
| 470 | |
| 471 } // namespace gfx | |
| OLD | NEW |