diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-13 16:43:03 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-13 16:43:03 +0000 |
commit | c6ac841f51c0b884b38e917ac30b1dfde0dc43a7 (patch) | |
tree | 2b490ffa6795f72e7232d658b766785f0de64e38 /gfx | |
parent | 6b32b95cff99ee72fd7824237ae5070263e5c496 (diff) | |
download | chromium_src-c6ac841f51c0b884b38e917ac30b1dfde0dc43a7.zip chromium_src-c6ac841f51c0b884b38e917ac30b1dfde0dc43a7.tar.gz chromium_src-c6ac841f51c0b884b38e917ac30b1dfde0dc43a7.tar.bz2 |
Rework gfx::Font by moving platform-specific code into inner classes.
gfx::Font is a platform-neutral API shim that exists as a wrapper object to allow for the creation and lifetime of gfx::Font objects to remain consistent with past usage.
gfx::PlatformFont is an interface implemented by the platform-specific inner classes (gfx::PlatformFontWin,Mac,Gtk).
BUG=none
TEST=existing unittests
Review URL: http://codereview.chromium.org/3083022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56040 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gfx')
-rw-r--r-- | gfx/canvas_skia_linux.cc | 11 | ||||
-rw-r--r-- | gfx/canvas_skia_mac.mm | 6 | ||||
-rw-r--r-- | gfx/canvas_skia_win.cc | 4 | ||||
-rw-r--r-- | gfx/font.cc | 85 | ||||
-rw-r--r-- | gfx/font.h | 268 | ||||
-rw-r--r-- | gfx/font_gtk.cc | 152 | ||||
-rw-r--r-- | gfx/font_mac.mm | 92 | ||||
-rw-r--r-- | gfx/font_skia.cc | 255 | ||||
-rw-r--r-- | gfx/font_unittest.cc | 42 | ||||
-rw-r--r-- | gfx/font_win.cc | 216 | ||||
-rw-r--r-- | gfx/gfx.gyp | 12 | ||||
-rw-r--r-- | gfx/native_widget_types.h | 7 | ||||
-rw-r--r-- | gfx/platform_font.h | 78 | ||||
-rw-r--r-- | gfx/platform_font_gtk.cc | 434 | ||||
-rw-r--r-- | gfx/platform_font_gtk.h | 111 | ||||
-rw-r--r-- | gfx/platform_font_mac.h | 57 | ||||
-rw-r--r-- | gfx/platform_font_mac.mm | 142 | ||||
-rw-r--r-- | gfx/platform_font_win.cc | 268 | ||||
-rw-r--r-- | gfx/platform_font_win.h | 131 |
19 files changed, 1401 insertions, 970 deletions
diff --git a/gfx/canvas_skia_linux.cc b/gfx/canvas_skia_linux.cc index d63f9b4..1a22af5 100644 --- a/gfx/canvas_skia_linux.cc +++ b/gfx/canvas_skia_linux.cc @@ -14,6 +14,7 @@ #include "base/utf_string_conversions.h" #include "gfx/font.h" #include "gfx/gtk_util.h" +#include "gfx/platform_font_gtk.h" #include "gfx/rect.h" namespace { @@ -149,7 +150,7 @@ static void SetupPangoLayout(PangoLayout* layout, resolution); } - PangoFontDescription* desc = gfx::Font::PangoFontFromGfxFont(font); + PangoFontDescription* desc = font.GetNativeFont(); pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); @@ -246,10 +247,12 @@ void CanvasSkia::DrawStringInt(const std::wstring& text, cairo_move_to(cr, x, y); pango_cairo_show_layout(cr, layout); - if (font.style() & gfx::Font::UNDERLINED) { + if (font.GetStyle() & gfx::Font::UNDERLINED) { + gfx::PlatformFontGtk* platform_font = + static_cast<gfx::PlatformFontGtk*>(font.platform_font()); double underline_y = - static_cast<double>(y) + height + font.underline_position(); - cairo_set_line_width(cr, font.underline_thickness()); + static_cast<double>(y) + height + platform_font->underline_position(); + cairo_set_line_width(cr, platform_font->underline_thickness()); cairo_move_to(cr, x, underline_y); cairo_line_to(cr, x + width, underline_y); cairo_stroke(cr); diff --git a/gfx/canvas_skia_mac.mm b/gfx/canvas_skia_mac.mm index 7836869..e2cb553 100644 --- a/gfx/canvas_skia_mac.mm +++ b/gfx/canvas_skia_mac.mm @@ -28,14 +28,14 @@ CanvasSkia::~CanvasSkia() { void CanvasSkia::SizeStringInt(const std::wstring& text, const gfx::Font& font, int *width, int *height, int flags) { - NSFont* native_font = font.nativeFont(); + NSFont* native_font = font.GetNativeFont(); NSString* ns_string = base::SysWideToNSString(text); NSDictionary* attributes = [NSDictionary dictionaryWithObject:native_font forKey:NSFontAttributeName]; NSSize string_size = [ns_string sizeWithAttributes:attributes]; *width = string_size.width; - *height = font.height(); + *height = font.GetHeight(); } void CanvasSkia::DrawStringInt(const std::wstring& text, const gfx::Font& font, @@ -59,7 +59,7 @@ void CanvasSkia::DrawStringInt(const std::wstring& text, const gfx::Font& font, NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys: - font.nativeFont(), NSFontAttributeName, + font.GetNativeFont(), NSFontAttributeName, ns_color, NSForegroundColorAttributeName, ns_style, NSParagraphStyleAttributeName, nil]; diff --git a/gfx/canvas_skia_win.cc b/gfx/canvas_skia_win.cc index f3c5f8f..cc8a7eb 100644 --- a/gfx/canvas_skia_win.cc +++ b/gfx/canvas_skia_win.cc @@ -163,7 +163,7 @@ void CanvasSkia::SizeStringInt(const std::wstring& text, RECT r = { 0, 0, *width, *height }; HDC dc = GetDC(NULL); - HFONT old_font = static_cast<HFONT>(SelectObject(dc, font.hfont())); + HFONT old_font = static_cast<HFONT>(SelectObject(dc, font.GetNativeFont())); DoDrawText(dc, clamped_string, &r, ComputeFormatFlags(flags, clamped_string) | DT_CALCRECT); SelectObject(dc, old_font); @@ -212,7 +212,7 @@ void CanvasSkia::DrawStringInt(const std::wstring& text, const gfx::Font& font, const SkColor& color, int x, int y, int w, int h, int flags) { - DrawStringInt(text, font.hfont(), color, x, y, w, h, flags); + DrawStringInt(text, font.GetNativeFont(), color, x, y, w, h, flags); } // Checks each pixel immediately adjacent to the given pixel in the bitmap. If diff --git a/gfx/font.cc b/gfx/font.cc new file mode 100644 index 0000000..625968a --- /dev/null +++ b/gfx/font.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 "gfx/font.h" + +#include "gfx/platform_font.h" + +namespace gfx { + +//////////////////////////////////////////////////////////////////////////////// +// Font, public: + +Font::Font() : platform_font_(PlatformFont::CreateDefault()) { +} + +Font::Font(const Font& other) : platform_font_(other.platform_font_) { +} + +gfx::Font& Font::operator=(const Font& other) { + platform_font_ = other.platform_font_; + return *this; +} + +Font::Font(NativeFont native_font) + : platform_font_(PlatformFont::CreateFromNativeFont(native_font)) { +} + +Font::Font(PlatformFont* platform_font) : platform_font_(platform_font) { +} + +Font::Font(const std::wstring& font_name, int font_size) + : platform_font_(PlatformFont::CreateFromNameAndSize(font_name, + font_size)) { +} + +Font::~Font() { +} + +Font Font::DeriveFont(int size_delta) const { + return DeriveFont(size_delta, GetStyle()); +} + +Font Font::DeriveFont(int size_delta, int style) const { + return platform_font_->DeriveFont(size_delta, style); +} + +int Font::GetHeight() const { + return platform_font_->GetHeight(); +} + +int Font::GetBaseline() const { + return platform_font_->GetBaseline(); +} + +int Font::GetAverageCharacterWidth() const { + return platform_font_->GetAverageCharacterWidth(); +} + +int Font::GetStringWidth(const std::wstring& text) const { + return platform_font_->GetStringWidth(text); +} + +int Font::GetExpectedTextWidth(int length) const { + return platform_font_->GetExpectedTextWidth(length); +} + +int Font::GetStyle() const { + return platform_font_->GetStyle(); +} + +const std::wstring& Font::GetFontName() const { + return platform_font_->GetFontName(); +} + +int Font::GetFontSize() const { + return platform_font_->GetFontSize(); +} + +NativeFont Font::GetNativeFont() const { + return platform_font_->GetNativeFont(); +} + +} // namespace gfx + @@ -6,63 +6,50 @@ #define GFX_FONT_H_ #pragma once -#include "build/build_config.h" - #include <string> -#if defined(OS_WIN) -typedef struct HFONT__* HFONT; -#elif !defined(OS_MACOSX) -#include "third_party/skia/include/core/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; -#else -typedef struct _PangoFontDescription PangoFontDescription; -class SkTypeface; -typedef SkTypeface* NativeFont; -#endif - -#include "base/basictypes.h" #include "base/ref_counted.h" -#include "base/scoped_ptr.h" +#include "gfx/native_widget_types.h" namespace gfx { +class PlatformFont; + // Font provides a wrapper around an underlying font. Copy and assignment // operators are explicitly allowed, and cheap. class Font { public: // The following constants indicate the font style. - enum { + enum FontStyle { NORMAL = 0, BOLD = 1, ITALIC = 2, UNDERLINED = 4, }; - // Creates a Font given font name (e.g. arial), font size (e.g. 12). - // Skia actually expects a family name and not a font name. - static Font CreateFont(const std::wstring& font_name, int font_size); + // Creates a font with the default name and style. + Font(); + + // Creates a font that is a clone of another font object. + Font(const Font& other); + gfx::Font& operator=(const Font& other); + + // Creates a font from the specified native font. + explicit Font(NativeFont native_font); - ~Font() { } + // Construct a Font object with the specified PlatformFont object. The Font + // object takes ownership of the PlatformFont object. + explicit Font(PlatformFont* platform_font); + + // Creates a font with the specified name and size. + Font(const std::wstring& font_name, int font_size); + + ~Font(); // 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. - Font DeriveFont(int size_delta) const { - return DeriveFont(size_delta, style()); - } + Font DeriveFont(int size_delta) const; // Returns a new Font derived from the existing font. // size_delta is the size to add to the current font. See the single @@ -76,212 +63,51 @@ class Font { // greater than just ascent + descent. Specifically, the Windows and Mac // implementations include leading and the Linux one does not. This may // need to be revisited in the future. - int height() const; + int GetHeight() const; // Returns the baseline, or ascent, of the font. - int baseline() const; + int GetBaseline() const; // Returns the average character width for the font. - int ave_char_width() const; + int GetAverageCharacterWidth() 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. + // 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. - const std::wstring& FontName() const; + int GetStyle() const; - // Font Size. - int FontSize(); + // Returns the font name. + const std::wstring& GetFontName() const; - NativeFont nativeFont() const; + // Returns the font size in pixels. + int GetFontSize() const; - // Creates a font with the default name and style. - Font(); - -#if defined(OS_WIN) - // Creates a Font from the specified HFONT. The supplied HFONT is effectively - // copied. - static Font CreateFont(HFONT hfont); - - // Returns the handle to the underlying HFONT. This is used by gfx::Canvas 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; - } - - // Callback that returns the minimum height that should be used for - // gfx::Fonts. Optional. If not specified, the minimum font size is 0. - typedef int (*GetMinimumFontSizeCallback)(); - static GetMinimumFontSizeCallback get_minimum_font_size_callback; + // Returns the native font handle. + // Lifetime lore: + // Windows: This handle is owned by the Font object, and should not be + // destroyed by the caller. + // Mac: Caller must release this object. + // Gtk: This handle is created on demand, and must be freed by calling + // pango_font_description_free() when the caller is done using it. + NativeFont GetNativeFont() const; - // Callback that adjusts a LOGFONT to meet suitability requirements of the - // embedding application. Optional. If not specified, no adjustments are - // performed other than clamping to a minimum font height if - // |get_minimum_font_size_callback| is specified. - typedef void (*AdjustFontCallback)(LOGFONT* lf); - static AdjustFontCallback adjust_font_callback; - -#elif !defined(OS_MACOSX) - static Font CreateFont(PangoFontDescription* desc); - // We need a copy constructor and assignment operator to deal with - // the Skia reference counting. - Font(const Font& other); - Font& operator=(const Font& other); - // Setup a Skia context to use the current typeface - void PaintSetup(SkPaint* paint) const; - - // Converts |gfx_font| to a new pango font. Free the returned font with - // pango_font_description_free(). - static PangoFontDescription* PangoFontFromGfxFont(const gfx::Font& gfx_font); - - // Position as an offset from the height of the drawn text, used to draw - // an underline. This is a negative number, so the underline would be - // drawn at y + height + underline_position; - double underline_position() const; - // The thickness to draw the underline. - double underline_thickness() const; -#endif + // Raw access to the underlying platform font implementation. Can be + // static_cast to a known implementation type if needed. + PlatformFont* platform_font() const { return platform_font_.get(); } 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, Font indirectly refers to the HFONT by way of - // an HFontRef. That is, every Font 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 Font 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); - - // 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_; } - const std::wstring& font_name() const { return font_name_; } - - private: - friend class base::RefCounted<HFontRef>; - - ~HFontRef(); - - 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_; - std::wstring font_name_; - - 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 Font(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_MACOSX) - explicit Font(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 CopyFont(const Font& other); - - // The default font, used for the default constructor. - static Font* default_font_; - - // Return the scale factor for fonts that account for DPI. - static float GetPangoScaleFactor(); - - // The average width of a character, initialized and cached if needed. - double avg_width() const; - - // Potentially slow call to get pango metrics (avg width, underline info). - void InitPangoMetrics(); - - // 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_; - - // The pango metrics are much more expensive so we wait until we need them - // to compute them. - bool pango_metrics_inited_; - double avg_width_; - double underline_position_; - double underline_thickness_; -#else // OS_MACOSX - explicit Font(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 - + // Wrapped platform font implementation. + scoped_refptr<PlatformFont> platform_font_; }; } // namespace gfx #endif // GFX_FONT_H_ + diff --git a/gfx/font_gtk.cc b/gfx/font_gtk.cc index 644a7ff..e69de29 100644 --- a/gfx/font_gtk.cc +++ b/gfx/font_gtk.cc @@ -1,152 +0,0 @@ -// 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 "gfx/font.h" - -#include <algorithm> -#include <fontconfig/fontconfig.h> -#include <gtk/gtk.h> - -#include "base/logging.h" -#include "base/string_piece.h" -#include "base/utf_string_conversions.h" -#include "gfx/gtk_util.h" - -namespace gfx { - -Font* Font::default_font_ = NULL; - -// 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. -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; -} - -// 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. -float Font::GetPangoScaleFactor() { - 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; -} - -// static -Font Font::CreateFont(PangoFontDescription* desc) { - 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 - // a SkTypeface for the default font. - // TODO(agl): remove this. - std::wstring font_family = FindBestMatchFontFamilyName(family_name); - - Font font = CreateFont(font_family, size / PANGO_SCALE); - int style = 0; - if (pango_font_description_get_weight(desc) == PANGO_WEIGHT_BOLD) { - // TODO(davemoore) What should we do about other weights? We currently - // only support BOLD. - style |= BOLD; - } - if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC) { - // TODO(davemoore) What about PANGO_STYLE_OBLIQUE? - style |= ITALIC; - } - if (style != 0) { - font = font.DeriveFont(0, style); - } - return Font(font); -} - -// Get the default gtk system font (name and size). -Font::Font() { - 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(CreateFont(desc)); - pango_font_description_free(desc); - g_free(font_name); - - DCHECK(default_font_); - } - - CopyFont(*default_font_); -} - -// static -PangoFontDescription* Font::PangoFontFromGfxFont( - const gfx::Font& gfx_font) { - gfx::Font font = gfx_font; // Copy so we can call non-const methods. - PangoFontDescription* pfd = pango_font_description_new(); - pango_font_description_set_family(pfd, WideToUTF8(font.FontName()).c_str()); - // Set the absolute size to avoid overflowing UI elements. - pango_font_description_set_absolute_size(pfd, - font.FontSize() * PANGO_SCALE * Font::GetPangoScaleFactor()); - - switch (font.style()) { - 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; -} - -} // namespace gfx diff --git a/gfx/font_mac.mm b/gfx/font_mac.mm deleted file mode 100644 index e569a35..0000000 --- a/gfx/font_mac.mm +++ /dev/null @@ -1,92 +0,0 @@ -// 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 <Cocoa/Cocoa.h> - -#include "base/logging.h" -#include "base/scoped_nsobject.h" -#include "base/sys_string_conversions.h" -#include "gfx/canvas_skia.h" - -namespace gfx { - -// static -Font Font::CreateFont(const std::wstring& font_name, int font_size) { - return Font(font_name, font_size, NORMAL); -} - -Font::Font(const std::wstring& font_name, int font_size, int style) - : font_name_(font_name), - font_size_(font_size), - style_(style) { - calculateMetrics(); -} - -Font::Font() - : font_size_([NSFont systemFontSize]), - style_(NORMAL) { - NSFont* system_font = [NSFont systemFontOfSize:font_size_]; - font_name_ = base::SysNSStringToWide([system_font fontName]); - calculateMetrics(); -} - -void Font::calculateMetrics() { - NSFont* font = nativeFont(); - scoped_nsobject<NSLayoutManager> layout_manager( - [[NSLayoutManager alloc] init]); - height_ = [layout_manager defaultLineHeightForFont:font]; - ascent_ = [font ascender]; - avg_width_ = [font boundingRectForGlyph:[font glyphWithName:@"x"]].size.width; -} - -Font Font::DeriveFont(int size_delta, int style) const { - return Font(font_name_, font_size_ + size_delta, style); -} - -int Font::height() const { - return height_; -} - -int Font::baseline() const { - return ascent_; -} - -int Font::ave_char_width() const { - return avg_width_; -} - -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; -} - -int Font::GetExpectedTextWidth(int length) const { - return length * avg_width_; -} - -int Font::style() const { - return style_; -} - -const std::wstring& Font::FontName() const { - return font_name_; -} - -int Font::FontSize() { - return font_size_; -} - -NativeFont Font::nativeFont() const { - // TODO(pinkerton): apply |style_| to font. http://crbug.com/34667 - // 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_]; -} - -} // namespace gfx diff --git a/gfx/font_skia.cc b/gfx/font_skia.cc deleted file mode 100644 index 0289cdb..0000000 --- a/gfx/font_skia.cc +++ /dev/null @@ -1,255 +0,0 @@ -// 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 diff --git a/gfx/font_unittest.cc b/gfx/font_unittest.cc index 6e63951..d2eda1c 100644 --- a/gfx/font_unittest.cc +++ b/gfx/font_unittest.cc @@ -14,35 +14,35 @@ class FontTest : public testing::Test { }; TEST_F(FontTest, LoadArial) { - Font cf(Font::CreateFont(L"Arial", 16)); - ASSERT_TRUE(cf.nativeFont()); - ASSERT_EQ(cf.style(), Font::NORMAL); - ASSERT_EQ(cf.FontSize(), 16); - ASSERT_EQ(cf.FontName(), L"Arial"); + Font cf(L"Arial", 16); + ASSERT_TRUE(cf.GetNativeFont()); + ASSERT_EQ(cf.GetStyle(), Font::NORMAL); + ASSERT_EQ(cf.GetFontSize(), 16); + ASSERT_EQ(cf.GetFontName(), L"Arial"); } TEST_F(FontTest, LoadArialBold) { - Font cf(Font::CreateFont(L"Arial", 16)); + Font cf(L"Arial", 16); Font bold(cf.DeriveFont(0, Font::BOLD)); - ASSERT_TRUE(bold.nativeFont()); - ASSERT_EQ(bold.style(), Font::BOLD); + ASSERT_TRUE(bold.GetNativeFont()); + ASSERT_EQ(bold.GetStyle(), Font::BOLD); } TEST_F(FontTest, Ascent) { - Font cf(Font::CreateFont(L"Arial", 16)); - ASSERT_GT(cf.baseline(), 2); - ASSERT_LE(cf.baseline(), 22); + Font cf(L"Arial", 16); + ASSERT_GT(cf.GetBaseline(), 2); + ASSERT_LE(cf.GetBaseline(), 22); } TEST_F(FontTest, Height) { - Font cf(Font::CreateFont(L"Arial", 16)); - ASSERT_GE(cf.height(), 16); + Font cf(L"Arial", 16); + ASSERT_GE(cf.GetHeight(), 16); // TODO(akalin): Figure out why height is so large on Linux. - ASSERT_LE(cf.height(), 26); + ASSERT_LE(cf.GetHeight(), 26); } TEST_F(FontTest, AvgWidths) { - Font cf(Font::CreateFont(L"Arial", 16)); + Font cf(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)); @@ -50,7 +50,7 @@ TEST_F(FontTest, AvgWidths) { } TEST_F(FontTest, Widths) { - Font cf(Font::CreateFont(L"Arial", 16)); + Font cf(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")); @@ -61,20 +61,20 @@ TEST_F(FontTest, Widths) { // http://crbug.com/46733 TEST_F(FontTest, FAILS_DeriveFontResizesIfSizeTooSmall) { // This creates font of height -8. - Font cf(Font::CreateFont(L"Arial", 6)); + Font cf(L"Arial", 6); Font derived_font = cf.DeriveFont(-4); LOGFONT font_info; - GetObject(derived_font.hfont(), sizeof(LOGFONT), &font_info); + GetObject(derived_font.GetNativeFont(), sizeof(LOGFONT), &font_info); EXPECT_EQ(-5, font_info.lfHeight); } TEST_F(FontTest, DeriveFontKeepsOriginalSizeIfHeightOk) { // This creates font of height -8. - Font cf(Font::CreateFont(L"Arial", 6)); + Font cf(L"Arial", 6); Font derived_font = cf.DeriveFont(-2); LOGFONT font_info; - GetObject(derived_font.hfont(), sizeof(LOGFONT), &font_info); + GetObject(derived_font.GetNativeFont(), sizeof(LOGFONT), &font_info); EXPECT_EQ(-6, font_info.lfHeight); } #endif -} // anonymous namespace +} // namespace diff --git a/gfx/font_win.cc b/gfx/font_win.cc deleted file mode 100644 index f9b7243..0000000 --- a/gfx/font_win.cc +++ /dev/null @@ -1,216 +0,0 @@ -// 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 <windows.h> -#include <math.h> - -#include <algorithm> - -#include "base/logging.h" -#include "base/string_util.h" -#include "base/win_util.h" -#include "gfx/canvas_skia.h" - -namespace gfx { - -// static -Font::HFontRef* Font::base_font_ref_; - -// static -Font::AdjustFontCallback Font::adjust_font_callback = NULL; -Font::GetMinimumFontSizeCallback Font::get_minimum_font_size_callback = NULL; - -// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the -// font is bold. -static const int kTextMetricWeightBold = 700; - -// Returns either minimum font allowed for a current locale or -// lf_height + size_delta value. -static int AdjustFontSize(int lf_height, int size_delta) { - if (lf_height < 0) { - lf_height -= size_delta; - } else { - lf_height += size_delta; - } - int min_font_size = 0; - if (Font::get_minimum_font_size_callback) - min_font_size = Font::get_minimum_font_size_callback(); - // Make sure lf_height is not smaller than allowed min font size for current - // locale. - if (abs(lf_height) < min_font_size) { - return lf_height < 0 ? -min_font_size : min_font_size; - } else { - return lf_height; - } -} - -// -// Font -// - -Font::Font() - : font_ref_(GetBaseFontRef()) { -} - -int Font::height() const { - return font_ref_->height(); -} - -int Font::baseline() const { - return font_ref_->baseline(); -} - -int Font::ave_char_width() const { - return font_ref_->ave_char_width(); -} - -int Font::GetExpectedTextWidth(int length) const { - return length * std::min(font_ref_->dlu_base_x(), ave_char_width()); -} - -int Font::style() const { - return font_ref_->style(); -} - -NativeFont Font::nativeFont() const { - return hfont(); -} - -// static -Font Font::CreateFont(HFONT font) { - DCHECK(font); - LOGFONT font_info; - GetObject(font, sizeof(LOGFONT), &font_info); - return Font(CreateHFontRef(CreateFontIndirect(&font_info))); -} - -Font Font::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 Font::CreateFont(hf); -} - -// static -Font::HFontRef* Font::GetBaseFontRef() { - if (base_font_ref_ == NULL) { - NONCLIENTMETRICS metrics; - win_util::GetNonClientMetrics(&metrics); - - if (adjust_font_callback) - adjust_font_callback(&metrics.lfMessageFont); - metrics.lfMessageFont.lfHeight = - AdjustFontSize(metrics.lfMessageFont.lfHeight, 0); - HFONT font = CreateFontIndirect(&metrics.lfMessageFont); - DLOG_ASSERT(font); - base_font_ref_ = Font::CreateHFontRef(font); - // base_font_ref_ is global, up the ref count so it's never deleted. - base_font_ref_->AddRef(); - } - return base_font_ref_; -} - -const std::wstring& Font::FontName() const { - return font_ref_->font_name(); -} - -int Font::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; -} - -Font::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); - - LOGFONT font_info; - GetObject(hfont_, sizeof(LOGFONT), &font_info); - font_name_ = std::wstring(font_info.lfFaceName); -} - -Font::HFontRef::~HFontRef() { - DeleteObject(hfont_); -} - -Font Font::DeriveFont(int size_delta, int style) const { - LOGFONT font_info; - GetObject(hfont(), sizeof(LOGFONT), &font_info); - font_info.lfHeight = AdjustFontSize(font_info.lfHeight, size_delta); - 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 Font(CreateHFontRef(hfont)); -} - -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; -} - -Font::HFontRef* Font::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 |= Font::ITALIC; - } - if (font_metrics.tmUnderlined) { - style |= Font::UNDERLINED; - } - if (font_metrics.tmWeight >= kTextMetricWeightBold) { - style |= Font::BOLD; - } - - return new HFontRef(font, height, baseline, ave_char_width, style, - dlu_base_x); -} - -} // namespace gfx diff --git a/gfx/gfx.gyp b/gfx/gfx.gyp index 5947c4a..9b7e4aa 100644 --- a/gfx/gfx.gyp +++ b/gfx/gfx.gyp @@ -97,9 +97,7 @@ 'color_utils.h', 'favicon_size.h', 'font.h', - 'font_gtk.cc', - 'font_mac.mm', - 'font_win.cc', + 'font.cc', 'gfx_paths.cc', 'gfx_paths.h', 'insets.cc', @@ -109,6 +107,13 @@ 'path.h', 'path_gtk.cc', 'path_win.cc', + 'platform_font.h', + 'platform_font_gtk.h', + 'platform_font_gtk.cc', + 'platform_font_mac.h', + 'platform_font_mac.mm', + 'platform_font_win.h', + 'platform_font_win.cc', 'point.cc', 'point.h', 'rect.cc', @@ -151,7 +156,6 @@ '../build/linux/system.gyp:gtk', ], 'sources': [ - 'font_skia.cc', 'gtk_native_view_id_manager.cc', 'gtk_native_view_id_manager.h', 'gtk_util.cc', diff --git a/gfx/native_widget_types.h b/gfx/native_widget_types.h index 5cc032a..51b0885 100644 --- a/gfx/native_widget_types.h +++ b/gfx/native_widget_types.h @@ -35,18 +35,22 @@ #if defined(OS_WIN) #include <windows.h> +typedef struct HFONT__* HFONT; #elif defined(OS_MACOSX) struct CGContext; #ifdef __OBJC__ +@class NSFont; @class NSView; @class NSWindow; @class NSTextField; #else +class NSFont; class NSView; class NSWindow; class NSTextField; #endif // __OBJC__ #elif defined(TOOLKIT_USES_GTK) +typedef struct _PangoFontDescription PangoFontDescription; typedef struct _GdkCursor GdkCursor; typedef struct _GdkRegion GdkRegion; typedef struct _GtkWidget GtkWidget; @@ -57,6 +61,7 @@ typedef struct _cairo cairo_t; namespace gfx { #if defined(OS_WIN) +typedef HFONT NativeFont; typedef HWND NativeView; typedef HWND NativeWindow; typedef HWND NativeEditView; @@ -65,6 +70,7 @@ typedef HCURSOR NativeCursor; typedef HMENU NativeMenu; typedef HRGN NativeRegion; #elif defined(OS_MACOSX) +typedef NSFont* NativeFont; typedef NSView* NativeView; typedef NSWindow* NativeWindow; typedef NSTextField* NativeEditView; @@ -72,6 +78,7 @@ typedef CGContext* NativeDrawingContext; typedef void* NativeCursor; typedef void* NativeMenu; #elif defined(USE_X11) +typedef PangoFontDescription* NativeFont; typedef GtkWidget* NativeView; typedef GtkWindow* NativeWindow; typedef GtkWidget* NativeEditView; diff --git a/gfx/platform_font.h b/gfx/platform_font.h new file mode 100644 index 0000000..df3c7f2 --- /dev/null +++ b/gfx/platform_font.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef GFX_PLATFORM_FONT_ +#define GFX_PLATFORM_FONT_ +#pragma once + +#include <string> + +#include "base/ref_counted.h" +#include "gfx/native_widget_types.h" + +namespace gfx { + +class Font; + +class PlatformFont : public base::RefCounted<PlatformFont> { + public: + // Create an appropriate PlatformFont implementation. + static PlatformFont* CreateDefault(); + static PlatformFont* CreateFromFont(const Font& other); + static PlatformFont* CreateFromNativeFont(NativeFont native_font); + static PlatformFont* CreateFromNameAndSize(const std::wstring& font_name, + int font_size); + + // Returns a new Font derived from the existing font. + // size_delta 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. + virtual Font DeriveFont(int size_delta, int style) const = 0; + + // Returns the number of vertical pixels needed to display characters from + // the specified font. This may include some leading, i.e. height may be + // greater than just ascent + descent. Specifically, the Windows and Mac + // implementations include leading and the Linux one does not. This may + // need to be revisited in the future. + virtual int GetHeight() const = 0; + + // Returns the baseline, or ascent, of the font. + virtual int GetBaseline() const = 0; + + // Returns the average character width for the font. + virtual int GetAverageCharacterWidth() const = 0; + + // Returns the number of horizontal pixels needed to display the specified + // string. + virtual int GetStringWidth(const std::wstring& text) const = 0; + + // Returns the expected number of horizontal pixels needed to display the + // specified length of characters. Call GetStringWidth() to retrieve the + // actual number. + virtual int GetExpectedTextWidth(int length) const = 0; + + // Returns the style of the font. + virtual int GetStyle() const = 0; + + // Returns the font name. + virtual const std::wstring& GetFontName() const = 0; + + // Returns the font size in pixels. + virtual int GetFontSize() const = 0; + + // Returns the native font handle. + virtual NativeFont GetNativeFont() const = 0; + + protected: + virtual ~PlatformFont() {} + + private: + friend class base::RefCounted<PlatformFont>; +}; + +} // namespace gfx + +#endif // GFX_PLATFORM_FONT_ + diff --git a/gfx/platform_font_gtk.cc b/gfx/platform_font_gtk.cc new file mode 100644 index 0000000..db2e0c0 --- /dev/null +++ b/gfx/platform_font_gtk.cc @@ -0,0 +1,434 @@ +// 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/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" + +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. +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; + } +} + +// 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* 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())); +} + +PlatformFontGtk::PlatformFontGtk(const Font& other) { + InitFromPlatformFont( + static_cast<PlatformFontGtk*>(other.platform_font())); +} + +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; +} + +PlatformFontGtk::PlatformFontGtk(const std::wstring& font_name, + int font_size) { + InitWithNameAndSize(font_name, font_size); +} + +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_; +} + +//////////////////////////////////////////////////////////////////////////////// +// 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 PlatformFontGtk::GetHeight() const { + return height_; +} + +int PlatformFontGtk::GetBaseline() const { + return ascent_; +} + +int PlatformFontGtk::GetAverageCharacterWidth() const { + return SkScalarRound(average_width_); +} + +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* 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. + typeface = SkTypeface::CreateFromName( + kFallbackFontFamilyName, SkTypeface::kNormal); + CHECK(typeface) << "Could not find any font: " + << base::SysWideToUTF8(font_name) + << ", " << kFallbackFontFamilyName; + fallback = base::SysUTF8ToWide(kFallbackFontFamilyName); + } + SkAutoUnref typeface_helper(typeface); + + InitWithTypefaceNameSizeAndStyle(typeface, + fallback.empty() ? font_name : fallback, + font_size, + gfx::Font::NORMAL); +} + +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; + + SkPaint paint; + SkPaint::FontMetrics metrics; + PaintSetup(&paint); + paint.getFontMetrics(&metrics); + + ascent_ = SkScalarCeil(-metrics.fAscent); + height_ = ascent_ + SkScalarCeil(metrics.fDescent); +} + +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 PlatformFontGtk::PaintSetup(SkPaint* paint) const { + paint->setAntiAlias(false); + paint->setSubpixelText(false); + paint->setTextSize( + SkFloatToScalar(font_size_ * PlatformFontGtk::GetPangoScaleFactor())); + paint->setTypeface(typeface_); + paint->setFakeBoldText((gfx::Font::BOLD & style_) && !typeface_->isBold()); + paint->setTextSkewX((gfx::Font::ITALIC & style_) && !typeface_->isItalic() ? + -SK_Scalar1/4 : 0); +} + +void PlatformFontGtk::InitPangoMetrics() { + if (!pango_metrics_inited_) { + pango_metrics_inited_ = true; + PangoFontDescription* pango_desc = GetNativeFont(); + 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; + average_width_ = std::min(pango_width, dialog_units); + pango_font_description_free(pango_desc); + } +} + + +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 PlatformFontGtk::GetAverageWidth() const { + const_cast<PlatformFontGtk*>(this)->InitPangoMetrics(); + return average_width_; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFont, public: + +// static +PlatformFont* PlatformFont::CreateDefault() { + return new PlatformFontGtk; +} + +// static +PlatformFont* PlatformFont::CreateFromFont(const Font& other) { + return new PlatformFontGtk(other); +} + +// static +PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { + return new PlatformFontGtk(native_font); +} + +// static +PlatformFont* PlatformFont::CreateFromNameAndSize(const std::wstring& font_name, + int font_size) { + return new PlatformFontGtk(font_name, font_size); +} + +} // namespace gfx diff --git a/gfx/platform_font_gtk.h b/gfx/platform_font_gtk.h new file mode 100644 index 0000000..3c507fb --- /dev/null +++ b/gfx/platform_font_gtk.h @@ -0,0 +1,111 @@ +// 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. + +#ifndef GFX_PLATFORM_FONT_GTK_ +#define GFX_PLATFORM_FONT_GTK_ +#pragma once + +#include "base/scoped_ptr.h" +#include "gfx/platform_font.h" +#include "third_party/skia/include/core/SkRefCnt.h" + +class SkTypeface; +class SkPaint; + +namespace gfx { + +class PlatformFontGtk : public PlatformFont { + public: + PlatformFontGtk(); + explicit PlatformFontGtk(const Font& other); + explicit PlatformFontGtk(NativeFont native_font); + PlatformFontGtk(const std::wstring& font_name, + int font_size); + + // Converts |gfx_font| to a new pango font. Free the returned font with + // pango_font_description_free(). + static PangoFontDescription* PangoFontFromGfxFont(const gfx::Font& gfx_font); + + // Position as an offset from the height of the drawn text, used to draw + // an underline. This is a negative number, so the underline would be + // drawn at y + height + underline_position; + double underline_position() const; + // The thickness to draw the underline. + double underline_thickness() const; + + // Overridden from PlatformFont: + virtual Font DeriveFont(int size_delta, int style) const; + virtual int GetHeight() const; + virtual int GetBaseline() const; + virtual int GetAverageCharacterWidth() const; + virtual int GetStringWidth(const std::wstring& text) const; + virtual int GetExpectedTextWidth(int length) const; + virtual int GetStyle() const; + virtual const std::wstring& GetFontName() const; + virtual int GetFontSize() const; + virtual NativeFont GetNativeFont() const; + + private: + // Create a new instance of this object with the specified properties. Called + // from DeriveFont. + PlatformFontGtk(SkTypeface* typeface, + const std::wstring& name, + int size, + int style); + virtual ~PlatformFontGtk() {} + + // Initialize this object. + void InitWithNameAndSize(const std::wstring& font_name, int font_size); + void InitWithTypefaceNameSizeAndStyle(SkTypeface* typeface, + const std::wstring& name, + int size, + int style); + void InitFromPlatformFont(const PlatformFontGtk* other); + + // Potentially slow call to get pango metrics (average width, underline info). + void InitPangoMetrics(); + + // Setup a Skia context to use the current typeface + void PaintSetup(SkPaint* paint) const; + + // Make |this| a copy of |other|. + void CopyFont(const Font& other); + + // Return the scale factor for fonts that account for DPI. + static float GetPangoScaleFactor(); + + // The average width of a character, initialized and cached if needed. + double GetAverageWidth() const; + + // 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_; + + // The pango metrics are much more expensive so we wait until we need them + // to compute them. + bool pango_metrics_inited_; + double average_width_; + double underline_position_; + double underline_thickness_; + + // The default font, used for the default constructor. + static Font* default_font_; +}; + +} // namespace gfx + +#endif // GFX_PLATFORM_FONT_GTK_ + diff --git a/gfx/platform_font_mac.h b/gfx/platform_font_mac.h new file mode 100644 index 0000000..71a8262 --- /dev/null +++ b/gfx/platform_font_mac.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef GFX_PLATFORM_FONT_MAC_ +#define GFX_PLATFORM_FONT_MAC_ +#pragma once + +#include "gfx/platform_font.h" + +namespace gfx { + +class PlatformFontMac : public PlatformFont { + public: + PlatformFontMac(); + explicit PlatformFontMac(const Font& other); + explicit PlatformFontMac(NativeFont native_font); + PlatformFontMac(const std::wstring& font_name, + int font_size); + + // Overridden from PlatformFont: + virtual Font DeriveFont(int size_delta, int style) const; + virtual int GetHeight() const; + virtual int GetBaseline() const; + virtual int GetAverageCharacterWidth() const; + virtual int GetStringWidth(const std::wstring& text) const; + virtual int GetExpectedTextWidth(int length) const; + virtual int GetStyle() const; + virtual const std::wstring& GetFontName() const; + virtual int GetFontSize() const; + virtual NativeFont GetNativeFont() const; + + private: + PlatformFontMac(const std::wstring& font_name, int font_size, int style); + virtual ~PlatformFontMac() {} + + // Initialize the object with the specified parameters. + void InitWithNameSizeAndStyle(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 average_width_; +}; + +} // namespace gfx + +#endif // GFX_PLATFORM_FONT_MAC_ diff --git a/gfx/platform_font_mac.mm b/gfx/platform_font_mac.mm new file mode 100644 index 0000000..89616c5 --- /dev/null +++ b/gfx/platform_font_mac.mm @@ -0,0 +1,142 @@ +// 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/platform_font_mac.h" + +#include <Cocoa/Cocoa.h> + +#include "base/logging.h" +#include "base/scoped_nsobject.h" +#include "base/sys_string_conversions.h" +#include "gfx/canvas_skia.h" +#include "gfx/font.h" + +namespace gfx { + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontMac, public: + +PlatformFontMac::PlatformFontMac() { + font_size_ = [NSFont systemFontSize]; + style_ = gfx::Font::NORMAL; + NSFont* system_font = [NSFont systemFontOfSize:font_size_]; + font_name_ = base::SysNSStringToWide([system_font fontName]); + CalculateMetrics(); +} + +PlatformFontMac::PlatformFontMac(const Font& other) { +} + +PlatformFontMac::PlatformFontMac(NativeFont native_font) { +} + +PlatformFontMac::PlatformFontMac(const std::wstring& font_name, + int font_size) { + InitWithNameSizeAndStyle(font_name, font_size, gfx::Font::NORMAL); +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontMac, PlatformFont implementation: + +Font PlatformFontMac::DeriveFont(int size_delta, int style) const { + return Font(new PlatformFontMac(font_name_, font_size_ + size_delta, style)); +} + +int PlatformFontMac::GetHeight() const { + return height_; +} + +int PlatformFontMac::GetBaseline() const { + return ascent_; +} + +int PlatformFontMac::GetAverageCharacterWidth() const { + return average_width_; +} + +int PlatformFontMac::GetStringWidth(const std::wstring& text) const { + int width = 0, height = 0; + CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontMac*>(this)), + &width, &height, gfx::Canvas::NO_ELLIPSIS); + return width; +} + +int PlatformFontMac::GetExpectedTextWidth(int length) const { + return length * average_width_; +} + +int PlatformFontMac::GetStyle() const { + return style_; +} + +const std::wstring& PlatformFontMac::GetFontName() const { + return font_name_; +} + +int PlatformFontMac::GetFontSize() const { + return font_size_; +} + +NativeFont PlatformFontMac::GetNativeFont() const { + // TODO(pinkerton): apply |style_| to font. http://crbug.com/34667 + // 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_]; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontMac, private: + +PlatformFontMac::PlatformFontMac(const std::wstring& font_name, + int font_size, + int style) { + InitWithNameSizeAndStyle(font_name, font_size, style); +} + +void PlatformFontMac::InitWithNameSizeAndStyle(const std::wstring& font_name, + int font_size, + int style) { + font_name_ = font_name; + font_size_ = font_size; + style_ = style; + CalculateMetrics(); +} + +void PlatformFontMac::CalculateMetrics() { + NSFont* font = GetNativeFont(); + scoped_nsobject<NSLayoutManager> layout_manager( + [[NSLayoutManager alloc] init]); + height_ = [layout_manager defaultLineHeightForFont:font]; + ascent_ = [font ascender]; + average_width_ = + [font boundingRectForGlyph:[font glyphWithName:@"x"]].size.width; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFont, public: + +// static +PlatformFont* PlatformFont::CreateDefault() { + return new PlatformFontMac; +} + +// static +PlatformFont* PlatformFont::CreateFromFont(const Font& other) { + return new PlatformFontMac(other); +} + +// static +PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { + return new PlatformFontMac(native_font); +} + +// static +PlatformFont* PlatformFont::CreateFromNameAndSize(const std::wstring& font_name, + int font_size) { + return new PlatformFontMac(font_name, font_size); +} + +} // namespace gfx + diff --git a/gfx/platform_font_win.cc b/gfx/platform_font_win.cc new file mode 100644 index 0000000..41cadde --- /dev/null +++ b/gfx/platform_font_win.cc @@ -0,0 +1,268 @@ +// 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/platform_font_win.h" + +#include <windows.h> +#include <math.h> + +#include <algorithm> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "gfx/canvas_skia.h" +#include "gfx/font.h" + +namespace { + +// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the +// font is bold. +const int kTextMetricWeightBold = 700; + +// Returns either minimum font allowed for a current locale or +// lf_height + size_delta value. +int AdjustFontSize(int lf_height, int size_delta) { + if (lf_height < 0) { + lf_height -= size_delta; + } else { + lf_height += size_delta; + } + int min_font_size = 0; + if (gfx::PlatformFontWin::get_minimum_font_size_callback) + min_font_size = gfx::PlatformFontWin::get_minimum_font_size_callback(); + // Make sure lf_height is not smaller than allowed min font size for current + // locale. + if (abs(lf_height) < min_font_size) { + return lf_height < 0 ? -min_font_size : min_font_size; + } else { + return lf_height; + } +} + +} // namespace + +namespace gfx { + +// static +PlatformFontWin::HFontRef* PlatformFontWin::base_font_ref_; + +// static +PlatformFontWin::AdjustFontCallback + PlatformFontWin::adjust_font_callback = NULL; +PlatformFontWin::GetMinimumFontSizeCallback + PlatformFontWin::get_minimum_font_size_callback = NULL; + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontWin, public + +PlatformFontWin::PlatformFontWin() : font_ref_(GetBaseFontRef()) { +} + +PlatformFontWin::PlatformFontWin(const Font& other) { + InitWithCopyOfHFONT(other.GetNativeFont()); +} + +PlatformFontWin::PlatformFontWin(NativeFont native_font) { + InitWithCopyOfHFONT(native_font); +} + +PlatformFontWin::PlatformFontWin(const std::wstring& font_name, + int font_size) { + InitWithFontNameAndSize(font_name, font_size); +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontWin, PlatformFont implementation: + +Font PlatformFontWin::DeriveFont(int size_delta, int style) const { + LOGFONT font_info; + GetObject(GetNativeFont(), sizeof(LOGFONT), &font_info); + font_info.lfHeight = AdjustFontSize(font_info.lfHeight, size_delta); + font_info.lfUnderline = + ((style & gfx::Font::UNDERLINED) == gfx::Font::UNDERLINED); + font_info.lfItalic = ((style & gfx::Font::ITALIC) == gfx::Font::ITALIC); + font_info.lfWeight = (style & gfx::Font::BOLD) ? FW_BOLD : FW_NORMAL; + + HFONT hfont = CreateFontIndirect(&font_info); + return Font(new PlatformFontWin(CreateHFontRef(hfont))); +} + +int PlatformFontWin::GetHeight() const { + return font_ref_->height(); +} + +int PlatformFontWin::GetBaseline() const { + return font_ref_->baseline(); +} + +int PlatformFontWin::GetAverageCharacterWidth() const { + return font_ref_->ave_char_width(); +} + +int PlatformFontWin::GetStringWidth(const std::wstring& text) const { + int width = 0, height = 0; + CanvasSkia::SizeStringInt(text, Font(const_cast<PlatformFontWin*>(this)), + &width, &height, gfx::Canvas::NO_ELLIPSIS); + return width; +} + +int PlatformFontWin::GetExpectedTextWidth(int length) const { + return length * std::min(font_ref_->dlu_base_x(), GetAverageCharacterWidth()); +} + +int PlatformFontWin::GetStyle() const { + return font_ref_->style(); +} + +const std::wstring& PlatformFontWin::GetFontName() const { + return font_ref_->font_name(); +} + +int PlatformFontWin::GetFontSize() const { + LOGFONT font_info; + GetObject(font_ref_->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; +} + +NativeFont PlatformFontWin::GetNativeFont() const { + return font_ref_->hfont(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Font, private: + +void PlatformFontWin::InitWithCopyOfHFONT(HFONT hfont) { + DCHECK(hfont); + LOGFONT font_info; + GetObject(hfont, sizeof(LOGFONT), &font_info); + font_ref_ = CreateHFontRef(CreateFontIndirect(&font_info)); +} + +void PlatformFontWin::InitWithFontNameAndSize(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()); + font_ref_ = CreateHFontRef(hf); +} + +// static +PlatformFontWin::HFontRef* PlatformFontWin::GetBaseFontRef() { + if (base_font_ref_ == NULL) { + NONCLIENTMETRICS metrics; + win_util::GetNonClientMetrics(&metrics); + + if (adjust_font_callback) + adjust_font_callback(&metrics.lfMessageFont); + metrics.lfMessageFont.lfHeight = + AdjustFontSize(metrics.lfMessageFont.lfHeight, 0); + HFONT font = CreateFontIndirect(&metrics.lfMessageFont); + DLOG_ASSERT(font); + base_font_ref_ = PlatformFontWin::CreateHFontRef(font); + // base_font_ref_ is global, up the ref count so it's never deleted. + base_font_ref_->AddRef(); + } + return base_font_ref_; +} + +PlatformFontWin::HFontRef* PlatformFontWin::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 |= Font::ITALIC; + if (font_metrics.tmUnderlined) + style |= Font::UNDERLINED; + if (font_metrics.tmWeight >= kTextMetricWeightBold) + style |= Font::BOLD; + + return new HFontRef(font, height, baseline, ave_char_width, style, + dlu_base_x); +} + +PlatformFontWin::PlatformFontWin(HFontRef* hfont_ref) : font_ref_(hfont_ref) { +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFontWin::HFontRef: + +PlatformFontWin::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); + + LOGFONT font_info; + GetObject(hfont_, sizeof(LOGFONT), &font_info); + font_name_ = std::wstring(font_info.lfFaceName); +} + +PlatformFontWin::HFontRef::~HFontRef() { + DeleteObject(hfont_); +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformFont, public: + +// static +PlatformFont* PlatformFont::CreateDefault() { + return new PlatformFontWin; +} + +// static +PlatformFont* PlatformFont::CreateFromFont(const Font& other) { + return new PlatformFontWin(other); +} + +// static +PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { + return new PlatformFontWin(native_font); +} + +// static +PlatformFont* PlatformFont::CreateFromNameAndSize(const std::wstring& font_name, + int font_size) { + return new PlatformFontWin(font_name, font_size); +} + +} // namespace gfx diff --git a/gfx/platform_font_win.h b/gfx/platform_font_win.h new file mode 100644 index 0000000..1fd89166 --- /dev/null +++ b/gfx/platform_font_win.h @@ -0,0 +1,131 @@ +// 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. + +#ifndef GFX_PLATFORM_FONT_WIN_ +#define GFX_PLATFORM_FONT_WIN_ +#pragma once + +#include "base/ref_counted.h" +#include "gfx/platform_font.h" + +namespace gfx { + +class PlatformFontWin : public PlatformFont { + public: + PlatformFontWin(); + explicit PlatformFontWin(const Font& other); + explicit PlatformFontWin(NativeFont native_font); + PlatformFontWin(const std::wstring& font_name, + int font_size); + + // Dialog units to pixels conversion. + // See http://support.microsoft.com/kb/145994 for details. + int horizontal_dlus_to_pixels(int dlus) const { + return dlus * font_ref_->dlu_base_x() / 4; + } + int vertical_dlus_to_pixels(int dlus) const { + return dlus * font_ref_->height() / 8; + } + + // Callback that returns the minimum height that should be used for + // gfx::Fonts. Optional. If not specified, the minimum font size is 0. + typedef int (*GetMinimumFontSizeCallback)(); + static GetMinimumFontSizeCallback get_minimum_font_size_callback; + + // Callback that adjusts a LOGFONT to meet suitability requirements of the + // embedding application. Optional. If not specified, no adjustments are + // performed other than clamping to a minimum font height if + // |get_minimum_font_size_callback| is specified. + typedef void (*AdjustFontCallback)(LOGFONT* lf); + static AdjustFontCallback adjust_font_callback; + + // Overridden from PlatformFont: + virtual Font DeriveFont(int size_delta, int style) const; + virtual int GetHeight() const; + virtual int GetBaseline() const; + virtual int GetAverageCharacterWidth() const; + virtual int GetStringWidth(const std::wstring& text) const; + virtual int GetExpectedTextWidth(int length) const; + virtual int GetStyle() const; + virtual const std::wstring& GetFontName() const; + virtual int GetFontSize() const; + virtual NativeFont GetNativeFont() const; + + private: + virtual ~PlatformFontWin() {} + + // 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, Font indirectly refers to the HFONT by way of + // an HFontRef. That is, every Font 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 Font 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); + + // 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_; } + const std::wstring& font_name() const { return font_name_; } + + private: + friend class base::RefCounted<HFontRef>; + + ~HFontRef(); + + 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_; + std::wstring font_name_; + + DISALLOW_COPY_AND_ASSIGN(HFontRef); + }; + + // Initializes this object with a copy of the specified HFONT. + void InitWithCopyOfHFONT(HFONT hfont); + + // Initializes this object with the specified font name and size. + void InitWithFontNameAndSize(const std::wstring& font_name, + int font_size); + + // 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); + + // Creates a new PlatformFontWin with the specified HFontRef. Used when + // constructing a Font from a HFONT we don't want to copy. + explicit PlatformFontWin(HFontRef* hfont_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_; +}; + +} // namespace gfx + +#endif // GFX_PLATFORM_FONT_WIN_ + |