Index: gfx/platform_font_gtk.cc |
=================================================================== |
--- gfx/platform_font_gtk.cc (revision 55264) |
+++ gfx/platform_font_gtk.cc (working copy) |
@@ -2,16 +2,22 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "gfx/font.h" |
+#include "gfx/platform_font_gtk.h" |
+#include <algorithm> |
+#include <fontconfig/fontconfig.h> |
#include <gdk/gdk.h> |
+#include <gtk/gtk.h> |
#include <map> |
#include <pango/pango.h> |
#include "base/logging.h" |
#include "base/string_piece.h" |
#include "base/sys_string_conversions.h" |
+#include "base/utf_string_conversions.h" |
#include "gfx/canvas_skia.h" |
+#include "gfx/font.h" |
+#include "gfx/gtk_util.h" |
#include "third_party/skia/include/core/SkTypeface.h" |
#include "third_party/skia/include/core/SkPaint.h" |
@@ -25,7 +31,7 @@ |
// Retrieves the pango metrics for a pango font description. Caches the metrics |
// and never frees them. The metrics objects are relatively small and |
// very expensive to look up. |
-static PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) { |
+PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) { |
static std::map<int, PangoFontMetrics*>* desc_to_metrics = NULL; |
static PangoContext* context = NULL; |
@@ -51,141 +57,301 @@ |
} |
} |
+// Find the best match font for |family_name| in the same way as Skia |
+// to make sure CreateFont() successfully creates a default font. In |
+// Skia, it only checks the best match font. If it failed to find |
+// one, SkTypeface will be NULL for that font family. It eventually |
+// causes a segfault. For example, family_name = "Sans" and system |
+// may have various fonts. The first font family in FcPattern will be |
+// "DejaVu Sans" but a font family returned by FcFontMatch will be "VL |
+// PGothic". In this case, SkTypeface for "Sans" returns NULL even if |
+// the system has a font for "Sans" font family. See FontMatch() in |
+// skia/ports/SkFontHost_fontconfig.cpp for more detail. |
+std::wstring FindBestMatchFontFamilyName(const char* family_name) { |
+ FcPattern* pattern = FcPatternCreate(); |
+ FcValue fcvalue; |
+ fcvalue.type = FcTypeString; |
+ char* family_name_copy = strdup(family_name); |
+ fcvalue.u.s = reinterpret_cast<FcChar8*>(family_name_copy); |
+ FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0); |
+ FcConfigSubstitute(0, pattern, FcMatchPattern); |
+ FcDefaultSubstitute(pattern); |
+ FcResult result; |
+ FcPattern* match = FcFontMatch(0, pattern, &result); |
+ DCHECK(match) << "Could not find font: " << family_name; |
+ FcChar8* match_family; |
+ FcPatternGetString(match, FC_FAMILY, 0, &match_family); |
+ |
+ std::wstring font_family = UTF8ToWide(reinterpret_cast<char*>(match_family)); |
+ FcPatternDestroy(match); |
+ FcPatternDestroy(pattern); |
+ free(family_name_copy); |
+ return font_family; |
+} |
+ |
} // namespace |
namespace gfx { |
-Font::Font(const Font& other) { |
- CopyFont(other); |
+Font* PlatformFontGtk::default_font_ = NULL; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// PlatformFontGtk, public: |
+ |
+PlatformFontGtk::PlatformFontGtk() { |
+ if (default_font_ == NULL) { |
+ GtkSettings* settings = gtk_settings_get_default(); |
+ |
+ gchar* font_name = NULL; |
+ g_object_get(settings, "gtk-font-name", &font_name, NULL); |
+ |
+ // Temporary CHECK for helping track down |
+ // http://code.google.com/p/chromium/issues/detail?id=12530 |
+ CHECK(font_name) << " Unable to get gtk-font-name for default font."; |
+ |
+ PangoFontDescription* desc = |
+ pango_font_description_from_string(font_name); |
+ default_font_ = new Font(desc); |
+ pango_font_description_free(desc); |
+ g_free(font_name); |
+ |
+ DCHECK(default_font_); |
+ } |
+ |
+ InitFromPlatformFont( |
+ static_cast<PlatformFontGtk*>(default_font_->platform_font())); |
} |
-Font& Font::operator=(const Font& other) { |
- CopyFont(other); |
- return *this; |
+PlatformFontGtk::PlatformFontGtk(const Font& other) { |
+ InitFromPlatformFont( |
+ static_cast<PlatformFontGtk*>(other.platform_font())); |
} |
-Font::Font(SkTypeface* tf, const std::wstring& font_family, int font_size, |
- int style) |
- : typeface_helper_(new SkAutoUnref(tf)), |
- typeface_(tf), |
- font_family_(font_family), |
- font_size_(font_size), |
- style_(style), |
- pango_metrics_inited_(false), |
- avg_width_(0.0), |
- underline_position_(0.0), |
- underline_thickness_(0.0) { |
- tf->ref(); |
- calculateMetrics(); |
+PlatformFontGtk::PlatformFontGtk(NativeFont native_font) { |
+ gint size = pango_font_description_get_size(native_font); |
+ const char* family_name = pango_font_description_get_family(native_font); |
+ |
+ // Find best match font for |family_name| to make sure we can get |
+ // a SkTypeface for the default font. |
+ // TODO(agl): remove this. |
+ std::wstring font_family = FindBestMatchFontFamilyName(family_name); |
+ |
+ InitWithNameAndSize(font_family, size / PANGO_SCALE); |
+ int style = 0; |
+ if (pango_font_description_get_weight(native_font) == PANGO_WEIGHT_BOLD) { |
+ // TODO(davemoore) What should we do about other weights? We currently |
+ // only support BOLD. |
+ style |= gfx::Font::BOLD; |
+ } |
+ if (pango_font_description_get_style(native_font) == PANGO_STYLE_ITALIC) { |
+ // TODO(davemoore) What about PANGO_STYLE_OBLIQUE? |
+ style |= gfx::Font::ITALIC; |
+ } |
+ if (style != 0) |
+ style_ = style; |
} |
-void Font::calculateMetrics() { |
- SkPaint paint; |
- SkPaint::FontMetrics metrics; |
- PaintSetup(&paint); |
- paint.getFontMetrics(&metrics); |
+PlatformFontGtk::PlatformFontGtk(const std::wstring& font_name, |
+ int font_size) { |
+ InitWithNameAndSize(font_name, font_size); |
+} |
- ascent_ = SkScalarCeil(-metrics.fAscent); |
- height_ = ascent_ + SkScalarCeil(metrics.fDescent); |
+double PlatformFontGtk::underline_position() const { |
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); |
+ return underline_position_; |
+} |
+double PlatformFontGtk::underline_thickness() const { |
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); |
+ return underline_thickness_; |
} |
-void Font::CopyFont(const Font& other) { |
- typeface_helper_.reset(new SkAutoUnref(other.typeface_)); |
- typeface_ = other.typeface_; |
- typeface_->ref(); |
- font_family_ = other.font_family_; |
- font_size_ = other.font_size_; |
- style_ = other.style_; |
- height_ = other.height_; |
- ascent_ = other.ascent_; |
- pango_metrics_inited_ = other.pango_metrics_inited_; |
- avg_width_ = other.avg_width_; |
- underline_position_ = other.underline_position_; |
- underline_thickness_ = other.underline_thickness_; |
+//////////////////////////////////////////////////////////////////////////////// |
+// PlatformFontGtk, PlatformFont implementation: |
+ |
+Font PlatformFontGtk::DeriveFont(int size_delta, int style) const { |
+ // If the delta is negative, if must not push the size below 1 |
+ if (size_delta < 0) |
+ DCHECK_LT(-size_delta, font_size_); |
+ |
+ if (style == style_) { |
+ // Fast path, we just use the same typeface at a different size |
+ return Font(new PlatformFontGtk(typeface_, |
+ font_family_, |
+ font_size_ + size_delta, |
+ style_)); |
+ } |
+ |
+ // If the style has changed we may need to load a new face |
+ int skstyle = SkTypeface::kNormal; |
+ if (gfx::Font::BOLD & style) |
+ skstyle |= SkTypeface::kBold; |
+ if (gfx::Font::ITALIC & style) |
+ skstyle |= SkTypeface::kItalic; |
+ |
+ SkTypeface* typeface = SkTypeface::CreateFromName( |
+ base::SysWideToUTF8(font_family_).c_str(), |
+ static_cast<SkTypeface::Style>(skstyle)); |
+ SkAutoUnref tf_helper(typeface); |
+ |
+ return Font(new PlatformFontGtk(typeface, |
+ font_family_, |
+ font_size_ + size_delta, |
+ style)); |
} |
-int Font::height() const { |
+int PlatformFontGtk::GetHeight() const { |
return height_; |
} |
-int Font::baseline() const { |
+int PlatformFontGtk::GetBaseline() const { |
return ascent_; |
} |
-int Font::ave_char_width() const { |
- return SkScalarRound(avg_width()); |
+int PlatformFontGtk::GetAverageCharacterWidth() const { |
+ return SkScalarRound(average_width_); |
} |
-Font Font::CreateFont(const std::wstring& font_family, int font_size) { |
+int PlatformFontGtk::GetStringWidth(const std::wstring& text) const { |
+ int width = 0, height = 0; |
+ CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontGtk*>(this)), |
+ &width, &height, gfx::Canvas::NO_ELLIPSIS); |
+ return width; |
+} |
+ |
+int PlatformFontGtk::GetExpectedTextWidth(int length) const { |
+ double char_width = const_cast<PlatformFontGtk*>(this)->GetAverageWidth(); |
+ return round(static_cast<float>(length) * char_width); |
+} |
+ |
+int PlatformFontGtk::GetStyle() const { |
+ return style_; |
+} |
+ |
+const std::wstring& PlatformFontGtk::GetFontName() const { |
+ return font_family_; |
+} |
+ |
+int PlatformFontGtk::GetFontSize() const { |
+ return font_size_; |
+} |
+ |
+NativeFont PlatformFontGtk::GetNativeFont() const { |
+ PangoFontDescription* pfd = pango_font_description_new(); |
+ pango_font_description_set_family(pfd, WideToUTF8(GetFontName()).c_str()); |
+ // Set the absolute size to avoid overflowing UI elements. |
+ pango_font_description_set_absolute_size(pfd, |
+ GetFontSize() * PANGO_SCALE * GetPangoScaleFactor()); |
+ |
+ switch (GetStyle()) { |
+ case gfx::Font::NORMAL: |
+ // Nothing to do, should already be PANGO_STYLE_NORMAL. |
+ break; |
+ case gfx::Font::BOLD: |
+ pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD); |
+ break; |
+ case gfx::Font::ITALIC: |
+ pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC); |
+ break; |
+ case gfx::Font::UNDERLINED: |
+ // TODO(deanm): How to do underlined? Where do we use it? Probably have |
+ // to paint it ourselves, see pango_font_metrics_get_underline_position. |
+ break; |
+ } |
+ |
+ return pfd; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// PlatformFontGtk, private: |
+ |
+PlatformFontGtk::PlatformFontGtk(SkTypeface* typeface, |
+ const std::wstring& name, |
+ int size, |
+ int style) { |
+ InitWithTypefaceNameSizeAndStyle(typeface, name, size, style); |
+} |
+ |
+void PlatformFontGtk::InitWithNameAndSize(const std::wstring& font_name, |
+ int font_size) { |
DCHECK_GT(font_size, 0); |
std::wstring fallback; |
- SkTypeface* tf = SkTypeface::CreateFromName( |
- base::SysWideToUTF8(font_family).c_str(), SkTypeface::kNormal); |
- if (!tf) { |
+ SkTypeface* typeface = SkTypeface::CreateFromName( |
+ base::SysWideToUTF8(font_name).c_str(), SkTypeface::kNormal); |
+ if (!typeface) { |
// A non-scalable font such as .pcf is specified. Falls back to a default |
// scalable font. |
- tf = SkTypeface::CreateFromName( |
+ typeface = SkTypeface::CreateFromName( |
kFallbackFontFamilyName, SkTypeface::kNormal); |
- CHECK(tf) << "Could not find any font: " |
- << base::SysWideToUTF8(font_family) |
- << ", " << kFallbackFontFamilyName; |
+ CHECK(typeface) << "Could not find any font: " |
+ << base::SysWideToUTF8(font_name) |
+ << ", " << kFallbackFontFamilyName; |
fallback = base::SysUTF8ToWide(kFallbackFontFamilyName); |
} |
- SkAutoUnref tf_helper(tf); |
+ SkAutoUnref typeface_helper(typeface); |
- return Font( |
- tf, fallback.empty() ? font_family : fallback, font_size, NORMAL); |
+ InitWithTypefaceNameSizeAndStyle(typeface, |
+ fallback.empty() ? font_name : fallback, |
+ font_size, |
+ gfx::Font::NORMAL); |
} |
-Font Font::DeriveFont(int size_delta, int style) const { |
- // If the delta is negative, if must not push the size below 1 |
- if (size_delta < 0) { |
- DCHECK_LT(-size_delta, font_size_); |
- } |
+void PlatformFontGtk::InitWithTypefaceNameSizeAndStyle( |
+ SkTypeface* typeface, |
+ const std::wstring& font_family, |
+ int font_size, |
+ int style) { |
+ typeface_helper_.reset(new SkAutoUnref(typeface)); |
+ typeface_ = typeface; |
+ typeface_->ref(); |
+ font_family_ = font_family; |
+ font_size_ = font_size; |
+ style_ = style; |
+ pango_metrics_inited_ = false; |
+ average_width_ = 0.0f; |
+ underline_position_ = 0.0f; |
+ underline_thickness_ = 0.0f; |
- if (style == style_) { |
- // Fast path, we just use the same typeface at a different size |
- return Font(typeface_, font_family_, font_size_ + size_delta, style_); |
- } |
+ SkPaint paint; |
+ SkPaint::FontMetrics metrics; |
+ PaintSetup(&paint); |
+ paint.getFontMetrics(&metrics); |
- // If the style has changed we may need to load a new face |
- int skstyle = SkTypeface::kNormal; |
- if (BOLD & style) |
- skstyle |= SkTypeface::kBold; |
- if (ITALIC & style) |
- skstyle |= SkTypeface::kItalic; |
+ ascent_ = SkScalarCeil(-metrics.fAscent); |
+ height_ = ascent_ + SkScalarCeil(metrics.fDescent); |
+} |
- SkTypeface* tf = SkTypeface::CreateFromName( |
- base::SysWideToUTF8(font_family_).c_str(), |
- static_cast<SkTypeface::Style>(skstyle)); |
- SkAutoUnref tf_helper(tf); |
- |
- return Font(tf, font_family_, font_size_ + size_delta, style); |
+void PlatformFontGtk::InitFromPlatformFont(const PlatformFontGtk* other) { |
+ typeface_helper_.reset(new SkAutoUnref(other->typeface_)); |
+ typeface_ = other->typeface_; |
+ typeface_->ref(); |
+ font_family_ = other->font_family_; |
+ font_size_ = other->font_size_; |
+ style_ = other->style_; |
+ height_ = other->height_; |
+ ascent_ = other->ascent_; |
+ pango_metrics_inited_ = other->pango_metrics_inited_; |
+ average_width_ = other->average_width_; |
+ underline_position_ = other->underline_position_; |
+ underline_thickness_ = other->underline_thickness_; |
} |
-void Font::PaintSetup(SkPaint* paint) const { |
+void PlatformFontGtk::PaintSetup(SkPaint* paint) const { |
paint->setAntiAlias(false); |
paint->setSubpixelText(false); |
- paint->setTextSize(SkFloatToScalar(font_size_ * Font::GetPangoScaleFactor())); |
+ paint->setTextSize( |
+ SkFloatToScalar(font_size_ * PlatformFontGtk::GetPangoScaleFactor())); |
paint->setTypeface(typeface_); |
- paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold()); |
- paint->setTextSkewX((ITALIC & style_) && !typeface_->isItalic() ? |
+ paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold()); |
+ paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ? |
-SK_Scalar1/4 : 0); |
} |
-int Font::GetStringWidth(const std::wstring& text) const { |
- int width = 0, height = 0; |
- CanvasSkia::SizeStringInt(text, *this, &width, &height, |
- gfx::Canvas::NO_ELLIPSIS); |
- return width; |
-} |
- |
-void Font::InitPangoMetrics() { |
+void PlatformFontGtk::InitPangoMetrics() { |
if (!pango_metrics_inited_) { |
pango_metrics_inited_ = true; |
- PangoFontDescription* pango_desc = PangoFontFromGfxFont(*this); |
+ PangoFontDescription* pango_desc = GetNativeFont(); |
PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc); |
underline_position_ = |
@@ -211,45 +377,58 @@ |
int text_width = GetStringWidth( |
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); |
double dialog_units = (text_width / 26 + 1) / 2; |
- avg_width_ = std::min(pango_width, dialog_units); |
+ average_width_ = std::min(pango_width, dialog_units); |
pango_font_description_free(pango_desc); |
} |
} |
-double Font::avg_width() const { |
- const_cast<Font*>(this)->InitPangoMetrics(); |
- return avg_width_; |
-} |
-double Font::underline_position() const { |
- const_cast<Font*>(this)->InitPangoMetrics(); |
- return underline_position_; |
+float PlatformFontGtk::GetPangoScaleFactor() { |
+ // Pango scales font sizes. This returns the scale factor. See |
+ // pango_cairo_context_set_resolution for details. |
+ // NOTE: this isn't entirely accurate, in that Pango also consults the |
+ // FC_PIXEL_SIZE first (see get_font_size in pangocairo-fcfont), but this |
+ // seems to give us the same sizes as used by Pango for all our fonts in both |
+ // English and Thai. |
+ static float scale_factor = gfx::GetPangoResolution(); |
+ static bool determined_scale = false; |
+ if (!determined_scale) { |
+ if (scale_factor <= 0) |
+ scale_factor = 1; |
+ else |
+ scale_factor /= 72.0; |
+ determined_scale = true; |
+ } |
+ return scale_factor; |
} |
-double Font::underline_thickness() const { |
- const_cast<Font*>(this)->InitPangoMetrics(); |
- return underline_thickness_; |
+double PlatformFontGtk::GetAverageWidth() const { |
+ const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); |
+ return average_width_; |
} |
-int Font::GetExpectedTextWidth(int length) const { |
- double char_width = const_cast<Font*>(this)->avg_width(); |
- return round(static_cast<float>(length) * char_width); |
-} |
+//////////////////////////////////////////////////////////////////////////////// |
+// PlatformFont, public: |
-int Font::style() const { |
- return style_; |
+// static |
+PlatformFont* PlatformFont::CreateDefault() { |
+ return new PlatformFontGtk; |
} |
-const std::wstring& Font::FontName() const { |
- return font_family_; |
+// static |
+PlatformFont* PlatformFont::CreateFromFont(const Font& other) { |
+ return new PlatformFontGtk(other); |
} |
-int Font::FontSize() { |
- return font_size_; |
+// static |
+PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { |
+ return new PlatformFontGtk(native_font); |
} |
-NativeFont Font::nativeFont() const { |
- return typeface_; |
+// static |
+PlatformFont* PlatformFont::CreateFromNameAndSize(const std::wstring& font_name, |
+ int font_size) { |
+ return new PlatformFontGtk(font_name, font_size); |
} |
} // namespace gfx |