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