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 <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
    347