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