blob: 0289cdb9ab8cb48cd063bd7d1ae6878d1784bd97 [file] [log] [blame]
// 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 <gdk/gdk.h>
#include <map>
#include <pango/pango.h>
#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<int, PangoFontMetrics*>* 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, PangoFontMetrics*>();
}
int desc_hash = pango_font_description_hash(desc);
std::map<int, PangoFontMetrics*>::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<SkTypeface::Style>(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<Font*>(this)->InitPangoMetrics();
return avg_width_;
}
double Font::underline_position() const {
const_cast<Font*>(this)->InitPangoMetrics();
return underline_position_;
}
double Font::underline_thickness() const {
const_cast<Font*>(this)->InitPangoMetrics();
return underline_thickness_;
}
int Font::GetExpectedTextWidth(int length) const {
double char_width = const_cast<Font*>(this)->avg_width();
return round(static_cast<float>(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