diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-06 01:13:41 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-06 01:13:41 +0000 |
commit | c601cd327760f385f03d228967d8aa7445712dd6 (patch) | |
tree | 3849dc1f70c468d26dc2543db85979eb8a5b8c1d /app/gfx | |
parent | 24e5219bf22811b32ca821c35c85f31e2697160a (diff) | |
download | chromium_src-c601cd327760f385f03d228967d8aa7445712dd6.zip chromium_src-c601cd327760f385f03d228967d8aa7445712dd6.tar.gz chromium_src-c601cd327760f385f03d228967d8aa7445712dd6.tar.bz2 |
move chrome_font to app/gfx
http://crbug.com/11387
Review URL: http://codereview.chromium.org/115010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15367 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app/gfx')
-rw-r--r-- | app/gfx/chrome_canvas.cc | 2 | ||||
-rw-r--r-- | app/gfx/chrome_canvas_linux.cc | 2 | ||||
-rw-r--r-- | app/gfx/chrome_canvas_win.cc | 2 | ||||
-rw-r--r-- | app/gfx/chrome_font.h | 237 | ||||
-rw-r--r-- | app/gfx/chrome_font_gtk.cc | 85 | ||||
-rw-r--r-- | app/gfx/chrome_font_mac.mm | 87 | ||||
-rw-r--r-- | app/gfx/chrome_font_skia.cc | 163 | ||||
-rw-r--r-- | app/gfx/chrome_font_unittest.cc | 57 | ||||
-rw-r--r-- | app/gfx/chrome_font_win.cc | 211 |
9 files changed, 843 insertions, 3 deletions
diff --git a/app/gfx/chrome_canvas.cc b/app/gfx/chrome_canvas.cc index 886a170..dfb1936 100644 --- a/app/gfx/chrome_canvas.cc +++ b/app/gfx/chrome_canvas.cc @@ -6,11 +6,11 @@ #include <limits> +#include "app/gfx/chrome_font.h" #include "app/l10n_util.h" #include "base/gfx/rect.h" #include "base/logging.h" #include "skia/include/SkShader.h" -#include "chrome/common/gfx/chrome_font.h" bool ChromeCanvas::GetClipRect(gfx::Rect* r) { SkRect clip; diff --git a/app/gfx/chrome_canvas_linux.cc b/app/gfx/chrome_canvas_linux.cc index 262c9db..ea5e371 100644 --- a/app/gfx/chrome_canvas_linux.cc +++ b/app/gfx/chrome_canvas_linux.cc @@ -6,10 +6,10 @@ #include <pango/pango.h> +#include "app/gfx/chrome_font.h" #include "base/gfx/rect.h" #include "base/logging.h" #include "base/string_util.h" -#include "chrome/common/gfx/chrome_font.h" namespace { diff --git a/app/gfx/chrome_canvas_win.cc b/app/gfx/chrome_canvas_win.cc index 4a4cd6a..76ca4d7 100644 --- a/app/gfx/chrome_canvas_win.cc +++ b/app/gfx/chrome_canvas_win.cc @@ -6,10 +6,10 @@ #include <limits> +#include "app/gfx/chrome_font.h" #include "app/l10n_util.h" #include "base/gfx/rect.h" #include "skia/include/SkShader.h" -#include "chrome/common/gfx/chrome_font.h" namespace { diff --git a/app/gfx/chrome_font.h b/app/gfx/chrome_font.h new file mode 100644 index 0000000..3672593 --- /dev/null +++ b/app/gfx/chrome_font.h @@ -0,0 +1,237 @@ +// Copyright (c) 2006-2008 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. + +#ifndef APP_GFX_CHROME_FONT_H_ +#define APP_GFX_CHROME_FONT_H_ + +#include "build/build_config.h" + +#include <string> + +#if defined(OS_WIN) +typedef struct HFONT__* HFONT; +#elif defined(OS_LINUX) +#include "skia/include/SkRefCnt.h" +class SkPaint; +class SkTypeface; +#endif + +#if defined(OS_WIN) +typedef struct HFONT__* NativeFont; +#elif defined(OS_MACOSX) +#ifdef __OBJC__ +@class NSFont; +#else +class NSFont; +#endif +typedef NSFont* NativeFont; +#elif defined(OS_LINUX) +class SkTypeface; +typedef SkTypeface* NativeFont; +#else // null port. +#error No known OS defined +#endif + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" + +// ChromeFont provides a wrapper around an underlying font. Copy and assignment +// operators are explicitly allowed, and cheap. +class ChromeFont { + public: + // The following constants indicate the font style. + enum { + NORMAL = 0, + BOLD = 1, + ITALIC = 2, + UNDERLINED = 4, + }; + + // Creates a ChromeFont given font name (e.g. arial), font size (e.g. 12). + // Skia actually expects a family name and not a font name. + static ChromeFont CreateFont(const std::wstring& font_name, int font_size); + + ~ChromeFont() { } + + // Returns a new Font derived from the existing font. + // size_deta is the size to add to the current font. For example, a value + // of 5 results in a font 5 units bigger than this font. + ChromeFont DeriveFont(int size_delta) const { + return DeriveFont(size_delta, style()); + } + + // Returns a new Font derived from the existing font. + // size_deta is the size to add to the current font. See the single + // argument version of this method for an example. + // The style parameter specifies the new style for the font, and is a + // bitmask of the values: BOLD, ITALIC and UNDERLINED. + ChromeFont DeriveFont(int size_delta, int style) const; + + // Returns the number of vertical pixels needed to display characters from + // the specified font. + int height() const; + + // Returns the baseline, or ascent, of the font. + int baseline() const; + + // Returns the average character width for the font. + int ave_char_width() const; + + // Returns the number of horizontal pixels needed to display the specified + // string. + int GetStringWidth(const std::wstring& text) const; + + // Returns the expected number of horizontal pixels needed to display + // the specified length of characters. + // Call GetStringWidth() to retrieve the actual number. + int GetExpectedTextWidth(int length) const; + + // Returns the style of the font. + int style() const; + + // Font Name. + // It is actually a font family name, because Skia expects a family name + // and not a font name. + std::wstring FontName(); + + // Font Size. + int FontSize(); + + NativeFont nativeFont() const; + + // Creates a font with the default name and style. + ChromeFont(); + +#if defined(OS_WIN) + // Creates a ChromeFont from the specified HFONT. The supplied HFONT is + // effectively copied. + static ChromeFont CreateFont(HFONT hfont); + + // Returns the handle to the underlying HFONT. This is used by ChromeCanvas to + // draw text. + HFONT hfont() const { return font_ref_->hfont(); } + + // Dialog units to pixels conversion. + // See http://support.microsoft.com/kb/145994 for details. + int horizontal_dlus_to_pixels(int dlus) { + return dlus * font_ref_->dlu_base_x() / 4; + } + int vertical_dlus_to_pixels(int dlus) { + return dlus * font_ref_->height() / 8; + } +#elif defined(OS_LINUX) + // We need a copy constructor and assignment operator to deal with + // the Skia reference counting. + ChromeFont(const ChromeFont& other); + ChromeFont& operator=(const ChromeFont& other); + // Setup a Skia context to use the current typeface + void PaintSetup(SkPaint* paint) const; +#endif + + private: + +#if defined(OS_WIN) + // Chrome text drawing bottoms out in the Windows GDI functions that take an + // HFONT (an opaque handle into Windows). To avoid lots of GDI object + // allocation and destruction, ChromeFont indirectly refers to the HFONT + // by way of an HFontRef. That is, every ChromeFont has an HFontRef, which + // has an HFONT. + // + // HFontRef is reference counted. Upon deletion, it deletes the HFONT. + // By making HFontRef maintain the reference to the HFONT, multiple + // HFontRefs can share the same HFONT, and ChromeFont can provide value + // semantics. + class HFontRef : public base::RefCounted<HFontRef> { + public: + // This constructor takes control of the HFONT, and will delete it when + // the HFontRef is deleted. + HFontRef(HFONT hfont, + int height, + int baseline, + int ave_char_width, + int style, + int dlu_base_x); + ~HFontRef(); + + // Accessors + HFONT hfont() const { return hfont_; } + int height() const { return height_; } + int baseline() const { return baseline_; } + int ave_char_width() const { return ave_char_width_; } + int style() const { return style_; } + int dlu_base_x() const { return dlu_base_x_; } + + private: + const HFONT hfont_; + const int height_; + const int baseline_; + const int ave_char_width_; + const int style_; + // Constants used in converting dialog units to pixels. + const int dlu_base_x_; + + DISALLOW_COPY_AND_ASSIGN(HFontRef); + }; + + // Returns the base font ref. This should ONLY be invoked on the + // UI thread. + static HFontRef* GetBaseFontRef(); + + // Creates and returns a new HFONTRef from the specified HFONT. + static HFontRef* CreateHFontRef(HFONT font); + + explicit ChromeFont(HFontRef* font_ref) : font_ref_(font_ref) { } + + // Reference to the base font all fonts are derived from. + static HFontRef* base_font_ref_; + + // Indirect reference to the HFontRef, which references the underlying HFONT. + scoped_refptr<HFontRef> font_ref_; +#elif defined(OS_LINUX) + explicit ChromeFont(SkTypeface* typeface, const std::wstring& name, + int size, int style); + // Calculate and cache the font metrics. + void calculateMetrics(); + // Make |this| a copy of |other|. + void CopyChromeFont(const ChromeFont& other); + + // The default font, used for the default constructor. + static ChromeFont* default_font_; + + // These two both point to the same SkTypeface. We use the SkAutoUnref to + // handle the reference counting, but without @typeface_ we would have to + // cast the SkRefCnt from @typeface_helper_ every time. + scoped_ptr<SkAutoUnref> typeface_helper_; + SkTypeface *typeface_; + + // Additional information about the face + // Skia actually expects a family name and not a font name. + std::wstring font_family_; + int font_size_; + int style_; + + // Cached metrics, generated at construction + int height_; + int ascent_; + int avg_width_; +#elif defined(OS_MACOSX) + explicit ChromeFont(const std::wstring& font_name, int font_size, int style); + + // Calculate and cache the font metrics. + void calculateMetrics(); + + std::wstring font_name_; + int font_size_; + int style_; + + // Cached metrics, generated at construction + int height_; + int ascent_; + int avg_width_; +#endif + +}; + +#endif // APP_GFX_CHROME_FONT_H_ diff --git a/app/gfx/chrome_font_gtk.cc b/app/gfx/chrome_font_gtk.cc new file mode 100644 index 0000000..39acf85 --- /dev/null +++ b/app/gfx/chrome_font_gtk.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 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 "app/gfx/chrome_font.h" + +#include <fontconfig/fontconfig.h> +#include <gtk/gtk.h> + +#include "base/string_util.h" + +ChromeFont* ChromeFont::default_font_ = NULL; + +// Find the best match font for |family_name| in the same way as Skia +// to make sure CreateFont() successfully creates default font. +// In Skia, it only checks the best match font. If it failed to find, +// SkTypeface will be NULL for that font family. It eventually causes 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 system has font +// for "Sans" font family. +// See FontMatch() in skia/ports/SkFontHost_fontconfig.cpp for more detail. +static 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; +} + +// Get the default gtk system font (name and size). +ChromeFont::ChromeFont() { + if (default_font_ == NULL) { + gtk_init(NULL, NULL); + GtkSettings* settings = gtk_settings_get_default(); + + GValue value = {0}; + g_value_init(&value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(settings), "gtk-font-name", &value); + + // gtk-font-name may be wrapped in quotes. + gchar* font_name = g_strdup_value_contents(&value); + gchar* font_ptr = font_name; + if (font_ptr[0] == '\"') + font_ptr++; + if (font_ptr[strlen(font_ptr) - 1] == '\"') + font_ptr[strlen(font_ptr) - 1] = '\0'; + + PangoFontDescription* desc = + pango_font_description_from_string(font_ptr); + gint size = pango_font_description_get_size(desc); + const char* family_name = pango_font_description_get_family(desc); + + // Find best match font for |family_name| to make sure we can get + // SkTypeface for default font. + // TODO(agl): remove this. + std::wstring font_family = FindBestMatchFontFamilyName(family_name); + + default_font_ = new ChromeFont(CreateFont(font_family, size / PANGO_SCALE)); + + pango_font_description_free(desc); + g_free(font_name); + g_value_unset(&value); + + DCHECK(default_font_); + } + + CopyChromeFont(*default_font_); +} diff --git a/app/gfx/chrome_font_mac.mm b/app/gfx/chrome_font_mac.mm new file mode 100644 index 0000000..87605ad --- /dev/null +++ b/app/gfx/chrome_font_mac.mm @@ -0,0 +1,87 @@ +// Copyright (c) 2009 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 "app/gfx/chrome_font.h" + +#include <Cocoa/Cocoa.h> + +#include "base/logging.h" +#include "base/sys_string_conversions.h" + +// static +ChromeFont ChromeFont::CreateFont(const std::wstring& font_name, + int font_size) { + return ChromeFont(font_name, font_size, NORMAL); +} + +ChromeFont::ChromeFont(const std::wstring& font_name, int font_size, int style) + : font_name_(font_name), + font_size_(font_size), + style_(style) { + calculateMetrics(); +} + +ChromeFont::ChromeFont() + : font_size_([NSFont systemFontSize]), + style_(NORMAL) { + NSFont* system_font = [NSFont systemFontOfSize:font_size_]; + font_name_ = base::SysNSStringToWide([system_font fontName]); + calculateMetrics(); +} + +void ChromeFont::calculateMetrics() { + NSFont* font = nativeFont(); + height_ = [font xHeight]; + ascent_ = [font ascender]; + avg_width_ = [font boundingRectForGlyph:[font glyphWithName:@"x"]].size.width; +} + +ChromeFont ChromeFont::DeriveFont(int size_delta, int style) const { + return ChromeFont(font_name_, font_size_ + size_delta, style); +} + +int ChromeFont::height() const { + return height_; +} + +int ChromeFont::baseline() const { + return ascent_; +} + +int ChromeFont::ave_char_width() const { + return avg_width_; +} + +int ChromeFont::GetStringWidth(const std::wstring& text) const { + NSFont* font = nativeFont(); + NSString* ns_string = base::SysWideToNSString(text); + NSDictionary* attributes = + [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; + NSSize string_size = [ns_string sizeWithAttributes:attributes]; + return string_size.width; +} + +int ChromeFont::GetExpectedTextWidth(int length) const { + return length * avg_width_; +} + +int ChromeFont::style() const { + return style_; +} + +std::wstring ChromeFont::FontName() { + return font_name_; +} + +int ChromeFont::FontSize() { + return font_size_; +} + +NativeFont ChromeFont::nativeFont() const { + // TODO(pinkerton): apply |style_| to font. + // We could cache this, but then we'd have to conditionally change the + // dtor just for MacOS. Not sure if we want to/need to do that. + return [NSFont fontWithName:base::SysWideToNSString(font_name_) + size:font_size_]; +} diff --git a/app/gfx/chrome_font_skia.cc b/app/gfx/chrome_font_skia.cc new file mode 100644 index 0000000..87f4ad6 --- /dev/null +++ b/app/gfx/chrome_font_skia.cc @@ -0,0 +1,163 @@ +// Copyright (c) 2006-2008 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 "app/gfx/chrome_font.h" + +#include "base/logging.h" +#include "base/sys_string_conversions.h" + +#include "skia/include/SkTypeface.h" +#include "skia/include/SkPaint.h" + +ChromeFont::ChromeFont(const ChromeFont& other) { + CopyChromeFont(other); +} + +ChromeFont& ChromeFont::operator=(const ChromeFont& other) { + CopyChromeFont(other); + return *this; +} + +ChromeFont::ChromeFont(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) { + tf->ref(); + calculateMetrics(); +} + +void ChromeFont::calculateMetrics() { + SkPaint paint; + SkPaint::FontMetrics metrics; + + PaintSetup(&paint); + paint.getFontMetrics(&metrics); + + ascent_ = SkScalarRound(-metrics.fAscent); + height_ = SkScalarRound(-metrics.fAscent + metrics.fDescent + + metrics.fLeading); + + if (metrics.fAvgCharWidth) { + avg_width_ = SkScalarRound(metrics.fAvgCharWidth); + } else { + static const char x_char = 'x'; + paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); + SkScalar width = paint.measureText(&x_char, 1); + + avg_width_ = static_cast<int>(ceilf(SkScalarToFloat(width))); + } +} + +void ChromeFont::CopyChromeFont(const ChromeFont& 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_; + avg_width_ = other.avg_width_; +} + +int ChromeFont::height() const { + return height_; +} + +int ChromeFont::baseline() const { + return ascent_; +} + +int ChromeFont::ave_char_width() const { + return avg_width_; +} + +ChromeFont ChromeFont::CreateFont(const std::wstring& font_family, + int font_size) { + DCHECK_GT(font_size, 0); + + SkTypeface* tf = SkTypeface::Create(base::SysWideToUTF8(font_family).c_str(), + SkTypeface::kNormal); + DCHECK(tf) << "Could not find font: " << base::SysWideToUTF8(font_family); + SkAutoUnref tf_helper(tf); + + return ChromeFont(tf, font_family, font_size, NORMAL); +} + +ChromeFont ChromeFont::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 ChromeFont(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::Create(base::SysWideToUTF8(font_family_).c_str(), + static_cast<SkTypeface::Style>(skstyle)); + SkAutoUnref tf_helper(tf); + + return ChromeFont(tf, font_family_, font_size_ + size_delta, skstyle); +} + +void ChromeFont::PaintSetup(SkPaint* paint) const { + paint->setAntiAlias(false); + paint->setSubpixelText(false); + paint->setTextSize(SkFloatToScalar(font_size_)); + paint->setTypeface(typeface_); + paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold()); + paint->setTextSkewX((ITALIC & style_) && !typeface_->isItalic() ? + -SK_Scalar1/4 : 0); +} + +int ChromeFont::GetStringWidth(const std::wstring& text) const { + const std::string utf8(base::SysWideToUTF8(text)); + + SkPaint paint; + PaintSetup(&paint); + paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); + SkScalar width = paint.measureText(utf8.data(), utf8.size()); + + int breadth = static_cast<int>(ceilf(SkScalarToFloat(width))); + // Check for overflow. We should probably be returning an unsigned + // int, but in practice we'll never have a screen massive enough + // to show that much text anyway. + if (breadth < 0) + return INT_MAX; + + return breadth; +} + +int ChromeFont::GetExpectedTextWidth(int length) const { + return length * avg_width_; +} + + +int ChromeFont::style() const { + return style_; +} + +std::wstring ChromeFont::FontName() { + return font_family_; +} + +int ChromeFont::FontSize() { + return font_size_; +} + +NativeFont ChromeFont::nativeFont() const { + return typeface_; +} diff --git a/app/gfx/chrome_font_unittest.cc b/app/gfx/chrome_font_unittest.cc new file mode 100644 index 0000000..5ad7973 --- /dev/null +++ b/app/gfx/chrome_font_unittest.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2006-2008 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 "app/gfx/chrome_font.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ChromeFontTest : public testing::Test { +}; + +TEST_F(ChromeFontTest, LoadArial) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ASSERT_TRUE(cf.nativeFont()); + ASSERT_EQ(cf.style(), ChromeFont::NORMAL); + ASSERT_EQ(cf.FontSize(), 16); + ASSERT_EQ(cf.FontName(), L"Arial"); +} + +TEST_F(ChromeFontTest, LoadArialBold) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ChromeFont bold(cf.DeriveFont(0, ChromeFont::BOLD)); + ASSERT_TRUE(bold.nativeFont()); + ASSERT_EQ(bold.style(), ChromeFont::BOLD); +} + +TEST_F(ChromeFontTest, Ascent) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ASSERT_GT(cf.baseline(), 2); + ASSERT_LT(cf.baseline(), 20); +} + +TEST_F(ChromeFontTest, Height) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ASSERT_GT(cf.baseline(), 2); + ASSERT_LT(cf.baseline(), 20); +} + +TEST_F(ChromeFontTest, AvgWidths) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ASSERT_EQ(cf.GetExpectedTextWidth(0), 0); + ASSERT_GT(cf.GetExpectedTextWidth(1), cf.GetExpectedTextWidth(0)); + ASSERT_GT(cf.GetExpectedTextWidth(2), cf.GetExpectedTextWidth(1)); + ASSERT_GT(cf.GetExpectedTextWidth(3), cf.GetExpectedTextWidth(2)); +} + +TEST_F(ChromeFontTest, Widths) { + ChromeFont cf(ChromeFont::CreateFont(L"Arial", 16)); + ASSERT_EQ(cf.GetStringWidth(L""), 0); + ASSERT_GT(cf.GetStringWidth(L"a"), cf.GetStringWidth(L"")); + ASSERT_GT(cf.GetStringWidth(L"ab"), cf.GetStringWidth(L"a")); + ASSERT_GT(cf.GetStringWidth(L"abc"), cf.GetStringWidth(L"ab")); +} + +} // anonymous namespace diff --git a/app/gfx/chrome_font_win.cc b/app/gfx/chrome_font_win.cc new file mode 100644 index 0000000..b5ddb18 --- /dev/null +++ b/app/gfx/chrome_font_win.cc @@ -0,0 +1,211 @@ +// Copyright (c) 2006-2008 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 "app/gfx/chrome_font.h" + +#include <windows.h> +#include <math.h> + +#include <algorithm> + +#include "app/l10n_util_win.h" +#include "base/logging.h" +#include "base/win_util.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" + +/*static*/ +ChromeFont::HFontRef* ChromeFont::base_font_ref_; + +// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the +// font is bold. +static const int kTextMetricWeightBold = 700; + +// +// ChromeFont +// + +ChromeFont::ChromeFont() + : font_ref_(GetBaseFontRef()) { +} + +int ChromeFont::height() const { + return font_ref_->height(); +} + +int ChromeFont::baseline() const { + return font_ref_->baseline(); +} + +int ChromeFont::ave_char_width() const { + return font_ref_->ave_char_width(); +} + +int ChromeFont::GetExpectedTextWidth(int length) const { + return length * std::min(font_ref_->dlu_base_x(), ave_char_width()); +} + +int ChromeFont::style() const { + return font_ref_->style(); +} + +NativeFont ChromeFont::nativeFont() const { + return hfont(); +} + +// static +ChromeFont ChromeFont::CreateFont(HFONT font) { + DCHECK(font); + LOGFONT font_info; + GetObject(font, sizeof(LOGFONT), &font_info); + return ChromeFont(CreateHFontRef(CreateFontIndirect(&font_info))); +} + +ChromeFont ChromeFont::CreateFont(const std::wstring& font_name, + int font_size) { + HDC hdc = GetDC(NULL); + long lf_height = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC(NULL, hdc); + HFONT hf = ::CreateFont(lf_height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + font_name.c_str()); + return ChromeFont::CreateFont(hf); +} + +// static +ChromeFont::HFontRef* ChromeFont::GetBaseFontRef() { + if (base_font_ref_ == NULL) { + NONCLIENTMETRICS metrics; + win_util::GetNonClientMetrics(&metrics); + + l10n_util::AdjustUIFont(&metrics.lfMessageFont); + + // See comment in ChromeFont::DeriveFont() about font size. + // TODO(jungshik): Add a per-locale resource entry for the minimum + // font size and actually enforce the lower-bound. 5 is way too small + // for CJK, Thai, and Indian locales. + DCHECK_GE(abs(metrics.lfMessageFont.lfHeight), 5); + HFONT font = CreateFontIndirect(&metrics.lfMessageFont); + DLOG_ASSERT(font); + base_font_ref_ = ChromeFont::CreateHFontRef(font); + // base_font_ref_ is global, up the ref count so it's never deleted. + base_font_ref_->AddRef(); + } + return base_font_ref_; +} + +std::wstring ChromeFont::FontName() { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + return (std::wstring(font_info.lfFaceName)); +} + +int ChromeFont::FontSize() { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + long lf_height = font_info.lfHeight; + HDC hdc = GetDC(NULL); + int device_caps = GetDeviceCaps(hdc, LOGPIXELSY); + int font_size = 0; + if (device_caps != 0) { + float font_size_float = -static_cast<float>(lf_height)*72/device_caps; + font_size = static_cast<int>(::ceil(font_size_float - 0.5)); + } + ReleaseDC(NULL, hdc); + return font_size; +} + +ChromeFont::HFontRef::HFontRef(HFONT hfont, + int height, + int baseline, + int ave_char_width, + int style, + int dlu_base_x) + : hfont_(hfont), + height_(height), + baseline_(baseline), + ave_char_width_(ave_char_width), + style_(style), + dlu_base_x_(dlu_base_x) { + DLOG_ASSERT(hfont); +} + +ChromeFont::HFontRef::~HFontRef() { + DeleteObject(hfont_); +} + + +ChromeFont ChromeFont::DeriveFont(int size_delta, int style) const { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + // LOGFONT returns two types of font heights, negative is measured slightly + // differently (character height, vs cell height). + if (font_info.lfHeight < 0) { + font_info.lfHeight -= size_delta; + } else { + font_info.lfHeight += size_delta; + } + // Even with "Small Fonts", the smallest readable font size is 5. It is easy + // to create a non-drawing font and forget about the fact that text should be + // drawn in the UI. This test ensures that the font will be readable. + DCHECK_GE(abs(font_info.lfHeight), 5); + font_info.lfUnderline = ((style & UNDERLINED) == UNDERLINED); + font_info.lfItalic = ((style & ITALIC) == ITALIC); + font_info.lfWeight = (style & BOLD) ? FW_BOLD : FW_NORMAL; + + HFONT hfont = CreateFontIndirect(&font_info); + return ChromeFont(CreateHFontRef(hfont)); +} + +int ChromeFont::GetStringWidth(const std::wstring& text) const { + int width = 0; + HDC dc = GetDC(NULL); + HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont())); + SIZE size; + if (GetTextExtentPoint32(dc, text.c_str(), static_cast<int>(text.size()), + &size)) { + width = size.cx; + } else { + width = 0; + } + SelectObject(dc, previous_font); + ReleaseDC(NULL, dc); + return width; +} + +ChromeFont::HFontRef* ChromeFont::CreateHFontRef(HFONT font) { + TEXTMETRIC font_metrics; + HDC screen_dc = GetDC(NULL); + HFONT previous_font = static_cast<HFONT>(SelectObject(screen_dc, font)); + int last_map_mode = SetMapMode(screen_dc, MM_TEXT); + GetTextMetrics(screen_dc, &font_metrics); + // Yes, this is how Microsoft recommends calculating the dialog unit + // conversions. + SIZE ave_text_size; + GetTextExtentPoint32(screen_dc, + L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + 52, &ave_text_size); + const int dlu_base_x = (ave_text_size.cx / 26 + 1) / 2; + // To avoid the DC referencing font_handle_, select the previous font. + SelectObject(screen_dc, previous_font); + SetMapMode(screen_dc, last_map_mode); + ReleaseDC(NULL, screen_dc); + + const int height = std::max(1, static_cast<int>(font_metrics.tmHeight)); + const int baseline = std::max(1, static_cast<int>(font_metrics.tmAscent)); + const int ave_char_width = + std::max(1, static_cast<int>(font_metrics.tmAveCharWidth)); + int style = 0; + if (font_metrics.tmItalic) { + style |= ChromeFont::ITALIC; + } + if (font_metrics.tmUnderlined) { + style |= ChromeFont::UNDERLINED; + } + if (font_metrics.tmWeight >= kTextMetricWeightBold) { + style |= ChromeFont::BOLD; + } + + return new HFontRef(font, height, baseline, ave_char_width, style, + dlu_base_x); +} |