Home | History | Annotate | Download | only in gfx
      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 <fontconfig/fontconfig.h>
      8 #include <pango/pango.h>
      9 
     10 #include <algorithm>
     11 #include <string>
     12 
     13 #include "base/logging.h"
     14 #include "base/strings/string_piece.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "grit/app_locale_settings.h"
     18 #include "third_party/skia/include/core/SkPaint.h"
     19 #include "third_party/skia/include/core/SkTypeface.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/gfx/canvas.h"
     22 #include "ui/gfx/font.h"
     23 #include "ui/gfx/pango_util.h"
     24 
     25 #if defined(TOOLKIT_GTK)
     26 #include <gdk/gdk.h>
     27 #include <gtk/gtk.h>
     28 #endif
     29 
     30 namespace {
     31 
     32 // The font family name which is used when a user's application font for
     33 // GNOME/KDE is a non-scalable one. The name should be listed in the
     34 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
     35 const char* kFallbackFontFamilyName = "sans";
     36 
     37 // Returns the available font family that best (in FontConfig's eyes) matches
     38 // the supplied list of family names.
     39 std::string FindBestMatchFontFamilyName(
     40     const std::vector<std::string>& family_names) {
     41   FcPattern* pattern = FcPatternCreate();
     42   for (std::vector<std::string>::const_iterator it = family_names.begin();
     43        it != family_names.end(); ++it) {
     44     FcValue fcvalue;
     45     fcvalue.type = FcTypeString;
     46     fcvalue.u.s = reinterpret_cast<const FcChar8*>(it->c_str());
     47     FcPatternAdd(pattern, FC_FAMILY, fcvalue, FcTrue /* append */);
     48   }
     49 
     50   FcConfigSubstitute(0, pattern, FcMatchPattern);
     51   FcDefaultSubstitute(pattern);
     52   FcResult result;
     53   FcPattern* match = FcFontMatch(0, pattern, &result);
     54   DCHECK(match) << "Could not find font";
     55   FcChar8* match_family = NULL;
     56   FcPatternGetString(match, FC_FAMILY, 0, &match_family);
     57   std::string font_family(reinterpret_cast<char*>(match_family));
     58   FcPatternDestroy(pattern);
     59   FcPatternDestroy(match);
     60   return font_family;
     61 }
     62 
     63 // Returns a Pango font description (suitable for parsing by
     64 // pango_font_description_from_string()) for the default UI font.
     65 std::string GetDefaultFont() {
     66 #if !defined(TOOLKIT_GTK)
     67 #if defined(OS_CHROMEOS)
     68   return l10n_util::GetStringUTF8(IDS_UI_FONT_FAMILY_CROS);
     69 #else
     70   return "sans 10";
     71 #endif    // defined(OS_CHROMEOS)
     72 #else
     73   GtkSettings* settings = gtk_settings_get_default();
     74 
     75   gchar* font_name = NULL;
     76   g_object_get(settings, "gtk-font-name", &font_name, NULL);
     77 
     78   // Temporary CHECK for helping track down
     79   // http://code.google.com/p/chromium/issues/detail?id=12530
     80   CHECK(font_name) << " Unable to get gtk-font-name for default font.";
     81 
     82   std::string default_font = std::string(font_name);
     83   g_free(font_name);
     84   return default_font;
     85 #endif  // !defined(TOOLKIT_GTK)
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace gfx {
     91 
     92 Font* PlatformFontPango::default_font_ = NULL;
     93 
     94 ////////////////////////////////////////////////////////////////////////////////
     95 // PlatformFontPango, public:
     96 
     97 PlatformFontPango::PlatformFontPango() {
     98   if (default_font_ == NULL) {
     99     std::string font_name = GetDefaultFont();
    100 
    101     ScopedPangoFontDescription desc(
    102         pango_font_description_from_string(font_name.c_str()));
    103     default_font_ = new Font(desc.get());
    104 
    105     DCHECK(default_font_);
    106   }
    107 
    108   InitFromPlatformFont(
    109       static_cast<PlatformFontPango*>(default_font_->platform_font()));
    110 }
    111 
    112 PlatformFontPango::PlatformFontPango(NativeFont native_font) {
    113   std::vector<std::string> family_names;
    114   base::SplitString(pango_font_description_get_family(native_font), ',',
    115                     &family_names);
    116   std::string font_family = FindBestMatchFontFamilyName(family_names);
    117   InitWithNameAndSize(font_family, gfx::GetPangoFontSizeInPixels(native_font));
    118 
    119   int style = 0;
    120   if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) {
    121     // TODO(davemoore) What should we do about other weights? We currently
    122     // only support BOLD.
    123     style |= gfx::Font::BOLD;
    124   }
    125   if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) {
    126     // TODO(davemoore) What about PANGO_STYLE_OBLIQUE?
    127     style |= gfx::Font::ITALIC;
    128   }
    129   if (style != 0)
    130     style_ = style;
    131 }
    132 
    133 PlatformFontPango::PlatformFontPango(const std::string& font_name,
    134                                      int font_size) {
    135   InitWithNameAndSize(font_name, font_size);
    136 }
    137 
    138 double PlatformFontPango::underline_position() const {
    139   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
    140   return underline_position_pixels_;
    141 }
    142 
    143 double PlatformFontPango::underline_thickness() const {
    144   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
    145   return underline_thickness_pixels_;
    146 }
    147 
    148 ////////////////////////////////////////////////////////////////////////////////
    149 // PlatformFontPango, PlatformFont implementation:
    150 
    151 // static
    152 void PlatformFontPango::ReloadDefaultFont() {
    153   delete default_font_;
    154   default_font_ = NULL;
    155 }
    156 
    157 Font PlatformFontPango::DeriveFont(int size_delta, int style) const {
    158   // If the delta is negative, if must not push the size below 1
    159   if (size_delta < 0)
    160     DCHECK_LT(-size_delta, font_size_pixels_);
    161 
    162   if (style == style_) {
    163     // Fast path, we just use the same typeface at a different size
    164     return Font(new PlatformFontPango(typeface_,
    165                                       font_family_,
    166                                       font_size_pixels_ + size_delta,
    167                                       style_));
    168   }
    169 
    170   // If the style has changed we may need to load a new face
    171   int skstyle = SkTypeface::kNormal;
    172   if (gfx::Font::BOLD & style)
    173     skstyle |= SkTypeface::kBold;
    174   if (gfx::Font::ITALIC & style)
    175     skstyle |= SkTypeface::kItalic;
    176 
    177   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
    178       SkTypeface::CreateFromName(
    179           font_family_.c_str(),
    180           static_cast<SkTypeface::Style>(skstyle)));
    181 
    182   return Font(new PlatformFontPango(typeface,
    183                                     font_family_,
    184                                     font_size_pixels_ + size_delta,
    185                                     style));
    186 }
    187 
    188 int PlatformFontPango::GetHeight() const {
    189   return height_pixels_;
    190 }
    191 
    192 int PlatformFontPango::GetBaseline() const {
    193   return ascent_pixels_;
    194 }
    195 
    196 int PlatformFontPango::GetAverageCharacterWidth() const {
    197   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
    198   return SkScalarRound(average_width_pixels_);
    199 }
    200 
    201 int PlatformFontPango::GetStringWidth(const base::string16& text) const {
    202   return Canvas::GetStringWidth(text,
    203                                 Font(const_cast<PlatformFontPango*>(this)));
    204 }
    205 
    206 int PlatformFontPango::GetExpectedTextWidth(int length) const {
    207   double char_width = const_cast<PlatformFontPango*>(this)->GetAverageWidth();
    208   return round(static_cast<float>(length) * char_width);
    209 }
    210 
    211 int PlatformFontPango::GetStyle() const {
    212   return style_;
    213 }
    214 
    215 std::string PlatformFontPango::GetFontName() const {
    216   return font_family_;
    217 }
    218 
    219 int PlatformFontPango::GetFontSize() const {
    220   return font_size_pixels_;
    221 }
    222 
    223 NativeFont PlatformFontPango::GetNativeFont() const {
    224   PangoFontDescription* pfd = pango_font_description_new();
    225   pango_font_description_set_family(pfd, GetFontName().c_str());
    226   // Set the absolute size to avoid overflowing UI elements.
    227   // pango_font_description_set_absolute_size() takes a size in Pango units.
    228   // There are PANGO_SCALE Pango units in one device unit.  Screen output
    229   // devices use pixels as their device units.
    230   pango_font_description_set_absolute_size(
    231       pfd, font_size_pixels_ * PANGO_SCALE);
    232 
    233   switch (GetStyle()) {
    234     case gfx::Font::NORMAL:
    235       // Nothing to do, should already be PANGO_STYLE_NORMAL.
    236       break;
    237     case gfx::Font::BOLD:
    238       pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
    239       break;
    240     case gfx::Font::ITALIC:
    241       pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
    242       break;
    243     case gfx::Font::UNDERLINE:
    244       // TODO(deanm): How to do underline?  Where do we use it?  Probably have
    245       // to paint it ourselves, see pango_font_metrics_get_underline_position.
    246       break;
    247   }
    248 
    249   return pfd;
    250 }
    251 
    252 ////////////////////////////////////////////////////////////////////////////////
    253 // PlatformFontPango, private:
    254 
    255 PlatformFontPango::PlatformFontPango(const skia::RefPtr<SkTypeface>& typeface,
    256                                      const std::string& name,
    257                                      int size,
    258                                      int style) {
    259   InitWithTypefaceNameSizeAndStyle(typeface, name, size, style);
    260 }
    261 
    262 PlatformFontPango::~PlatformFontPango() {}
    263 
    264 void PlatformFontPango::InitWithNameAndSize(const std::string& font_name,
    265                                             int font_size) {
    266   DCHECK_GT(font_size, 0);
    267   std::string fallback;
    268 
    269   skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(
    270           SkTypeface::CreateFromName(font_name.c_str(), SkTypeface::kNormal));
    271   if (!typeface) {
    272     // A non-scalable font such as .pcf is specified. Falls back to a default
    273     // scalable font.
    274     typeface = skia::AdoptRef(
    275         SkTypeface::CreateFromName(
    276             kFallbackFontFamilyName, SkTypeface::kNormal));
    277     CHECK(typeface) << "Could not find any font: "
    278                     << font_name
    279                     << ", " << kFallbackFontFamilyName;
    280     fallback = kFallbackFontFamilyName;
    281   }
    282 
    283   InitWithTypefaceNameSizeAndStyle(typeface,
    284                                    fallback.empty() ? font_name : fallback,
    285                                    font_size,
    286                                    gfx::Font::NORMAL);
    287 }
    288 
    289 void PlatformFontPango::InitWithTypefaceNameSizeAndStyle(
    290     const skia::RefPtr<SkTypeface>& typeface,
    291     const std::string& font_family,
    292     int font_size,
    293     int style) {
    294   typeface_ = typeface;
    295   font_family_ = font_family;
    296   font_size_pixels_ = font_size;
    297   style_ = style;
    298   pango_metrics_inited_ = false;
    299   average_width_pixels_ = 0.0f;
    300   underline_position_pixels_ = 0.0f;
    301   underline_thickness_pixels_ = 0.0f;
    302 
    303   SkPaint paint;
    304   SkPaint::FontMetrics metrics;
    305   PaintSetup(&paint);
    306   paint.getFontMetrics(&metrics);
    307 
    308   ascent_pixels_ = SkScalarCeil(-metrics.fAscent);
    309   height_pixels_ = ascent_pixels_ + SkScalarCeil(metrics.fDescent);
    310 }
    311 
    312 void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
    313   typeface_ = other->typeface_;
    314   font_family_ = other->font_family_;
    315   font_size_pixels_ = other->font_size_pixels_;
    316   style_ = other->style_;
    317   height_pixels_ = other->height_pixels_;
    318   ascent_pixels_ = other->ascent_pixels_;
    319   pango_metrics_inited_ = other->pango_metrics_inited_;
    320   average_width_pixels_ = other->average_width_pixels_;
    321   underline_position_pixels_ = other->underline_position_pixels_;
    322   underline_thickness_pixels_ = other->underline_thickness_pixels_;
    323 }
    324 
    325 void PlatformFontPango::PaintSetup(SkPaint* paint) const {
    326   paint->setAntiAlias(false);
    327   paint->setSubpixelText(false);
    328   paint->setTextSize(font_size_pixels_);
    329   paint->setTypeface(typeface_.get());
    330   paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold());
    331   paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ?
    332                       -SK_Scalar1/4 : 0);
    333 }
    334 
    335 void PlatformFontPango::InitPangoMetrics() {
    336   if (!pango_metrics_inited_) {
    337     pango_metrics_inited_ = true;
    338     ScopedPangoFontDescription pango_desc(GetNativeFont());
    339     PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
    340 
    341     underline_position_pixels_ =
    342         pango_font_metrics_get_underline_position(pango_metrics) /
    343         PANGO_SCALE;
    344 
    345     // TODO(davemoore): Come up with a better solution.
    346     // This is a hack, but without doing this the underlines
    347     // we get end up fuzzy. So we align to the midpoint of a pixel.
    348     underline_position_pixels_ /= 2;
    349 
    350     underline_thickness_pixels_ =
    351         pango_font_metrics_get_underline_thickness(pango_metrics) /
    352         PANGO_SCALE;
    353 
    354     // First get the Pango-based width (converting from Pango units to pixels).
    355     const double pango_width_pixels =
    356         pango_font_metrics_get_approximate_char_width(pango_metrics) /
    357         PANGO_SCALE;
    358 
    359     // Yes, this is how Microsoft recommends calculating the dialog unit
    360     // conversions.
    361     const int text_width_pixels = GetStringWidth(
    362         ASCIIToUTF16("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
    363     const double dialog_units_pixels = (text_width_pixels / 26 + 1) / 2;
    364     average_width_pixels_ = std::min(pango_width_pixels, dialog_units_pixels);
    365   }
    366 }
    367 
    368 
    369 double PlatformFontPango::GetAverageWidth() const {
    370   const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
    371   return average_width_pixels_;
    372 }
    373 
    374 ////////////////////////////////////////////////////////////////////////////////
    375 // PlatformFont, public:
    376 
    377 // static
    378 PlatformFont* PlatformFont::CreateDefault() {
    379   return new PlatformFontPango;
    380 }
    381 
    382 // static
    383 PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
    384   return new PlatformFontPango(native_font);
    385 }
    386 
    387 // static
    388 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name,
    389                                                   int font_size) {
    390   return new PlatformFontPango(font_name, font_size);
    391 }
    392 
    393 }  // namespace gfx
    394