From 310581b5d0f62adc135bdb61ff71cb63b0dc1be3 Mon Sep 17 00:00:00 2001 From: Lluis Sanchez Date: Thu, 28 Mar 2013 17:39:45 +0100 Subject: [PATCH] Font fixes Fixed methods that return the system default fonts. Added font tests. --- Samples/Samples/DrawingText.cs | 2 +- Testing/Tests/FontTests.cs | 222 +++++++++++++++++++ Testing/Tests/Tests.csproj | 1 + Xwt.Gtk/Xwt.GtkBackend/FontBackendHandler.cs | 58 +---- Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs | 20 -- Xwt/Xwt.Backends/FontBackendHandler.cs | 9 + Xwt/Xwt.Drawing/Font.cs | 71 ++++-- 7 files changed, 288 insertions(+), 95 deletions(-) create mode 100644 Testing/Tests/FontTests.cs diff --git a/Samples/Samples/DrawingText.cs b/Samples/Samples/DrawingText.cs index 32a3864d..b57b459c 100644 --- a/Samples/Samples/DrawingText.cs +++ b/Samples/Samples/DrawingText.cs @@ -49,7 +49,7 @@ namespace Samples var col1 = new Rectangle (); var col2 = new Rectangle (); - var text = new TextLayout (ctx); + var text = new TextLayout (); text.Font = this.Font.WithSize (24); Console.WriteLine (text.Font.Size); diff --git a/Testing/Tests/FontTests.cs b/Testing/Tests/FontTests.cs new file mode 100644 index 00000000..97931b28 --- /dev/null +++ b/Testing/Tests/FontTests.cs @@ -0,0 +1,222 @@ +// +// FontTests.cs +// +// Author: +// Lluis Sanchez +// +// Copyright (c) 2013 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +using NUnit.Framework; +using Xwt.Drawing; + +namespace Xwt +{ + [TestFixture] + public class FontTests + { + [Test] + public void FromNameEmpty () + { + var f = Font.FromName (""); + Assert.AreEqual (Font.SystemFont, f); + } + + [Test] + public void WithFamily () + { + var f1 = Font.SystemFont; + var f2 = f1.WithFamily ("Courier"); + Assert.AreEqual ("Courier", f2.Family); + Assert.AreEqual (f1.Size, f2.Size); + Assert.AreEqual (f1.Stretch, f2.Stretch); + Assert.AreEqual (f1.Style, f2.Style); + Assert.AreEqual (f1.Weight, f2.Weight); + } + + [Test] + public void WithSize () + { + var f1 = Font.SystemFont; + var f2 = f1.WithSize (33); + Assert.AreEqual (f1.Family, f2.Family); + Assert.AreEqual (33d, f2.Size); + Assert.AreEqual (f1.Stretch, f2.Stretch); + Assert.AreEqual (f1.Style, f2.Style); + Assert.AreEqual (f1.Weight, f2.Weight); + } + + [Test] + public void WithStretch () + { + var f1 = Font.SystemFont; + var f2 = f1.WithStretch (FontStretch.Condensed); + Assert.AreEqual (f1.Family, f2.Family); + Assert.AreEqual (f1.Size, f2.Size); + Assert.AreEqual (FontStretch.Condensed, f2.Stretch); + Assert.AreEqual (f1.Style, f2.Style); + Assert.AreEqual (f1.Weight, f2.Weight); + } + + [Test] + public void WithStyle () + { + var f1 = Font.SystemFont; + var f2 = f1.WithStyle (FontStyle.Oblique); + Assert.AreEqual (f1.Family, f2.Family); + Assert.AreEqual (f1.Size, f2.Size); + Assert.AreEqual (f1.Stretch, f2.Stretch); + Assert.AreEqual (FontStyle.Oblique, f2.Style); + Assert.AreEqual (f1.Weight, f2.Weight); + } + + [Test] + public void WithWeight () + { + var f1 = Font.SystemFont; + var f2 = f1.WithWeight (FontWeight.Bold); + Assert.AreEqual (f1.Family, f2.Family); + Assert.AreEqual (f1.Size, f2.Size); + Assert.AreEqual (f1.Stretch, f2.Stretch); + Assert.AreEqual (f1.Style, f2.Style); + Assert.AreEqual (FontWeight.Bold, f2.Weight); + } + + [Test] + public void FromNameOnlyFamily () + { + var f = Font.FromName ("Arial"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (Font.SystemFont.Size, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameOnlySize () + { + var f = Font.FromName ("33"); + Assert.AreEqual (Font.SystemFont.Family, f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameOnlyFamilyAndSize () + { + var f = Font.FromName ("Arial 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameOnlyFamilyAndSizeWithFallbackSecondChoice () + { + var f = Font.FromName ("Foobar, Arial, dummy, 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameOnlyFamilyAndSizeWithFallbackFirstChoice () + { + var f = Font.FromName ("Arial, Foobar, dummy 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameOnlyFamilyAndSizeWithFallbackNoChoice () + { + var f = Font.FromName ("Foobar, dummy 33"); + Assert.AreEqual (Font.SystemFont.Family, f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameWithStyle () + { + var f = Font.FromName ("Arial, dummy italic 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Italic, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameWithWeight () + { + var f = Font.FromName ("Arial, dummy bold 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Normal, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Bold, f.Weight); + } + + [Test] + public void FromNameWithStretch () + { + var f = Font.FromName ("Arial, dummy condensed 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Condensed, f.Stretch); + Assert.AreEqual (FontStyle.Normal, f.Style); + Assert.AreEqual (FontWeight.Normal, f.Weight); + } + + [Test] + public void FromNameWithAllStyles () + { + var f = Font.FromName ("Arial, dummy expanded Oblique Light 33"); + Assert.AreEqual ("Arial", f.Family); + Assert.AreEqual (33d, f.Size); + Assert.AreEqual (FontStretch.Expanded, f.Stretch); + Assert.AreEqual (FontStyle.Oblique, f.Style); + Assert.AreEqual (FontWeight.Light, f.Weight); + } + + [Test] + public void FontEquals () + { + var f1 = Font.FromName ("Arial expanded Oblique Light 33"); + var f2 = Font.FromName ("Arial expanded Oblique Light 33"); + Assert.IsTrue (f1.Equals (f2)); + } + } +} + diff --git a/Testing/Tests/Tests.csproj b/Testing/Tests/Tests.csproj index 746e7736..161cecf4 100644 --- a/Testing/Tests/Tests.csproj +++ b/Testing/Tests/Tests.csproj @@ -64,6 +64,7 @@ + diff --git a/Xwt.Gtk/Xwt.GtkBackend/FontBackendHandler.cs b/Xwt.Gtk/Xwt.GtkBackend/FontBackendHandler.cs index 8a4b1a2d..b160d03a 100644 --- a/Xwt.Gtk/Xwt.GtkBackend/FontBackendHandler.cs +++ b/Xwt.Gtk/Xwt.GtkBackend/FontBackendHandler.cs @@ -31,6 +31,7 @@ using Xwt.Drawing; using System.Globalization; using System.Reflection; using System.Collections.Generic; +using System.Linq; namespace Xwt.GtkBackend { @@ -38,67 +39,18 @@ namespace Xwt.GtkBackend { public override object GetSystemDefaultFont () { - if (Platform.IsMac || Platform.IsWindows) { - Xwt.Drawing.Font font = null; - GtkEngine.NativeToolkit.Invoke (delegate { - font = Xwt.Drawing.Font.SystemFont; - }); - return Create (font.Family, font.Size, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); - } - return null; - } - - public override object GetSystemDefaultMonospaceFont () - { - if (Platform.IsMac || Platform.IsWindows) { - Xwt.Drawing.Font font = null; - GtkEngine.NativeToolkit.Invoke (delegate { - font = Xwt.Drawing.Font.SystemMonospaceFont; - }); - return Create (font.Family, font.Size, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); - } - return null; - } - - public override object GetSystemDefaultSansSerifFont () - { - if (Platform.IsMac || Platform.IsWindows) { - Xwt.Drawing.Font font = null; - GtkEngine.NativeToolkit.Invoke (delegate { - font = Xwt.Drawing.Font.SystemSansSerifFont; - }); - return Create (font.Family, font.Size, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); - } - return null; - } - - public override object GetSystemDefaultSerifFont () - { - if (Platform.IsMac || Platform.IsWindows) { - Xwt.Drawing.Font font = null; - GtkEngine.NativeToolkit.Invoke (delegate { - font = Xwt.Drawing.Font.SystemSerifFont; - }); - return Create (font.Family, font.Size, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); - } - return null; + var la = new Gtk.Label (""); + return la.Style.FontDescription; } public override IEnumerable GetInstalledFonts () { - if (Platform.IsMac || Platform.IsWindows) { - IEnumerable fonts = null; - GtkEngine.NativeToolkit.Invoke (delegate { - fonts = Xwt.Drawing.Font.AvailableFontFamilies; - }); - return fonts; - } - return new string[0]; + return Gdk.PangoHelper.ContextGet ().FontMap.Families.Select (f => f.Name); } public override object Create (string fontName, double size, FontStyle style, FontWeight weight, FontStretch stretch) { - return FontDescription.FromString (fontName + " " + size.ToString (CultureInfo.InvariantCulture)); + return FontDescription.FromString (fontName + ", " + style + " " + weight + " " + stretch + " " + size.ToString (CultureInfo.InvariantCulture)); } #region IFontBackendHandler implementation diff --git a/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs b/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs index 3c526365..a3874f1b 100755 --- a/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs +++ b/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs @@ -33,31 +33,11 @@ namespace Xwt.GtkBackend { public class GtkEngine: ToolkitEngineBackend { - static Toolkit nativeToolkit; - public override void InitializeApplication () { Gtk.Application.Init (); } - public static Toolkit NativeToolkit { - get { - if (nativeToolkit == null) { - if (Platform.IsMac) { - if (!Toolkit.TryLoad (ToolkitType.XamMac, out nativeToolkit)) { - if (!Toolkit.TryLoad (ToolkitType.Cocoa, out nativeToolkit)) { - throw new InvalidOperationException ("Mac backend not found"); - } - } - } else if (Platform.IsWindows) { - if (!Toolkit.TryLoad (ToolkitType.Wpf, out nativeToolkit)) - throw new InvalidOperationException ("Windows backend not found"); - } - } - return nativeToolkit; - } - } - public override void InitializeBackends () { RegisterBackend (); diff --git a/Xwt/Xwt.Backends/FontBackendHandler.cs b/Xwt/Xwt.Backends/FontBackendHandler.cs index 2159ba45..d1cbfd18 100644 --- a/Xwt/Xwt.Backends/FontBackendHandler.cs +++ b/Xwt/Xwt.Backends/FontBackendHandler.cs @@ -112,7 +112,16 @@ namespace Xwt.Backends public abstract IEnumerable GetInstalledFonts (); + /// + /// Creates a new font. Returns null if the font family is not available in the system + /// + /// Font family name + /// Size in points + /// Style + /// Weight + /// Stretch public abstract object Create (string fontName, double size, FontStyle style, FontWeight weight, FontStretch stretch); + public abstract object Copy (object handle); public abstract object SetSize (object handle, double size); diff --git a/Xwt/Xwt.Drawing/Font.cs b/Xwt/Xwt.Drawing/Font.cs index 02f46dd3..f4dd583f 100644 --- a/Xwt/Xwt.Drawing/Font.cs +++ b/Xwt/Xwt.Drawing/Font.cs @@ -73,14 +73,12 @@ namespace Xwt.Drawing /// decimal number (size in points). Any one of the options may be absent. If FAMILY-LIST is absent, the default /// font family will be used. If STYLE-OPTIONS is missing, then all style options will be set to the default values. /// If SIZE is missing, the size in the resulting font description will be set to the default font size. + /// If the font doesn't exist, it returns the system font. /// public static Font FromName (string name) { var toolkit = Toolkit.CurrentEngine; var handler = toolkit.FontBackendHandler; - string[] parts = name.Split (new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length < 2) - throw new ArgumentException ("Font family name or size not specified"); double size = -1; FontStyle style = FontStyle.Normal; @@ -95,39 +93,50 @@ namespace Xwt.Drawing FontWeight fw; FontStretch fs; double siz; - if (Enum.TryParse (token, out st) && st != FontStyle.Normal) - style = st; - else if (Enum.TryParse (token, out fw) && fw != FontWeight.Normal) - weight = fw; - else if (Enum.TryParse (token, out fs) && fs != FontStretch.Normal) - stretch = fs; - else if (double.TryParse (token, out siz) && size != -1) + if (double.TryParse (token, out siz)) // Try parsing the number first, since Enum.TryParse can also parse numbers size = siz; + else if (Enum.TryParse (token, true, out st) && st != FontStyle.Normal) + style = st; + else if (Enum.TryParse (token, true, out fw) && fw != FontWeight.Normal) + weight = fw; + else if (Enum.TryParse (token, true, out fs) && fs != FontStretch.Normal) + stretch = fs; else if (token.Length > 0) break; + lasti = i; if (i <= 0) break; - lasti = i; + i = name.LastIndexOf (' ', i - 1); } while (true); - string fname = GetSupportedFont (name.Substring (0, lasti)); - if (fname.Length == 0) - fname = SystemFont.Family; + string fname = lasti > 0 ? name.Substring (0, lasti) : string.Empty; + fname = fname.Length > 0 ? GetSupportedFont (fname) : Font.SystemFont.Family; + if (size == -1) size = SystemFont.Size; - return new Font (handler.Create (fname, size, style, weight, stretch), toolkit); + var fb = handler.Create (fname, size, style, weight, stretch); + if (fb != null) + return new Font (fb, toolkit); + else + return Font.SystemFont; } static string GetSupportedFont (string fontNames) { - int i = fontNames.IndexOf (','); - if (i == -1) - return fontNames.Trim (); - LoadInstalledFonts (); + + int i = fontNames.IndexOf (','); + if (i == -1) { + var f = fontNames.Trim (); + if (installedFonts.Contains (f)) + return f; + else + return GetDefaultFont (f); + } + string[] names = fontNames.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries); if (names.Length == 0) throw new ArgumentException ("Font family name not provided"); @@ -137,10 +146,16 @@ namespace Xwt.Drawing if (installedFonts.Contains (n)) return n; } - return names[0].Trim (); + return GetDefaultFont (fontNames.Trim (' ',',')); } - static HashSet installedFonts = new HashSet (); + static string GetDefaultFont (string unknownFont) + { + Console.WriteLine ("Font '" + unknownFont + "' not available in the system. Using '" + Font.SystemFont.Family + "' instead"); + return Font.SystemFont.Family; + } + + static HashSet installedFonts; static ReadOnlyCollection installedFontsArray; @@ -262,6 +277,20 @@ namespace Xwt.Drawing sb.Append (' ').Append (Size.ToString (CultureInfo.InvariantCulture)); return sb.ToString (); } + + public override bool Equals (object obj) + { + var other = obj as Font; + if (other == null) + return false; + + return Family == other.Family && Style == other.Style && Weight == other.Weight && Stretch == other.Stretch && Size == other.Size; + } + + public override int GetHashCode () + { + return ToString().GetHashCode (); + } } public enum FontStyle