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