PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ui/gfx/platform_font_pango.cc

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