// Copyright (c) 2010 The Chromium Authors. All rights reserved. // 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 #include #include #include "base/logging.h" #include "base/string_piece.h" #include "base/sys_string_conversions.h" #include "gfx/canvas_skia.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/core/SkPaint.h" namespace { // The font family name which is used when a user's application font for // GNOME/KDE is a non-scalable one. The name should be listed in the // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp. const char* kFallbackFontFamilyName = "sans"; // 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) { static std::map* desc_to_metrics = NULL; static PangoContext* context = NULL; if (!context) { context = gdk_pango_context_get_for_screen(gdk_screen_get_default()); pango_context_set_language(context, pango_language_get_default()); } if (!desc_to_metrics) { desc_to_metrics = new std::map(); } int desc_hash = pango_font_description_hash(desc); std::map::iterator i = desc_to_metrics->find(desc_hash); if (i == desc_to_metrics->end()) { PangoFontMetrics* metrics = pango_context_get_metrics(context, desc, NULL); (*desc_to_metrics)[desc_hash] = metrics; return metrics; } else { return i->second; } } } // namespace namespace gfx { Font::Font(const Font& other) { CopyFont(other); } Font& Font::operator=(const Font& other) { CopyFont(other); return *this; } 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(); } void Font::calculateMetrics() { SkPaint paint; SkPaint::FontMetrics metrics; PaintSetup(&paint); paint.getFontMetrics(&metrics); ascent_ = SkScalarCeil(-metrics.fAscent); height_ = ascent_ + SkScalarCeil(metrics.fDescent); } 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_; } int Font::height() const { return height_; } int Font::baseline() const { return ascent_; } int Font::ave_char_width() const { return SkScalarRound(avg_width()); } Font Font::CreateFont(const std::wstring& font_family, 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) { // A non-scalable font such as .pcf is specified. Falls back to a default // scalable font. tf = SkTypeface::CreateFromName( kFallbackFontFamilyName, SkTypeface::kNormal); CHECK(tf) << "Could not find any font: " << base::SysWideToUTF8(font_family) << ", " << kFallbackFontFamilyName; fallback = base::SysUTF8ToWide(kFallbackFontFamilyName); } SkAutoUnref tf_helper(tf); return Font( tf, fallback.empty() ? font_family : fallback, font_size, 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_); } 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_); } // 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; SkTypeface* tf = SkTypeface::CreateFromName( base::SysWideToUTF8(font_family_).c_str(), static_cast(skstyle)); SkAutoUnref tf_helper(tf); return Font(tf, font_family_, font_size_ + size_delta, style); } void Font::PaintSetup(SkPaint* paint) const { paint->setAntiAlias(false); paint->setSubpixelText(false); paint->setTextSize(SkFloatToScalar(font_size_ * Font::GetPangoScaleFactor())); paint->setTypeface(typeface_); paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold()); paint->setTextSkewX((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() { if (!pango_metrics_inited_) { pango_metrics_inited_ = true; PangoFontDescription* pango_desc = PangoFontFromGfxFont(*this); PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc); underline_position_ = pango_font_metrics_get_underline_position(pango_metrics); underline_position_ /= PANGO_SCALE; // todo(davemoore) Come up with a better solution. // This is a hack, but without doing this the underlines // we get end up fuzzy. So we align to the midpoint of a pixel. underline_position_ /= 2; underline_thickness_ = pango_font_metrics_get_underline_thickness(pango_metrics); underline_thickness_ /= PANGO_SCALE; // First get the pango based width double pango_width = pango_font_metrics_get_approximate_char_width(pango_metrics); pango_width /= PANGO_SCALE; // Yes, this is how Microsoft recommends calculating the dialog unit // conversions. int text_width = GetStringWidth( L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); double dialog_units = (text_width / 26 + 1) / 2; avg_width_ = std::min(pango_width, dialog_units); pango_font_description_free(pango_desc); } } double Font::avg_width() const { const_cast(this)->InitPangoMetrics(); return avg_width_; } double Font::underline_position() const { const_cast(this)->InitPangoMetrics(); return underline_position_; } double Font::underline_thickness() const { const_cast(this)->InitPangoMetrics(); return underline_thickness_; } int Font::GetExpectedTextWidth(int length) const { double char_width = const_cast(this)->avg_width(); return round(static_cast(length) * char_width); } int Font::style() const { return style_; } const std::wstring& Font::FontName() const { return font_family_; } int Font::FontSize() { return font_size_; } NativeFont Font::nativeFont() const { return typeface_; } } // namespace gfx