Unify implementations of SystemFonts as much as possible. (dotnet/corefx#23668)
authorEric Mellino <erme@microsoft.com>
Wed, 30 Aug 2017 23:51:38 +0000 (16:51 -0700)
committerGitHub <noreply@github.com>
Wed, 30 Aug 2017 23:51:38 +0000 (16:51 -0700)
* Unify implementations of SystemFonts as much as possible.

* Add more basic tests for SystemFonts static property accessors.

* Mark a SystemFonts test as PlatformSpecific.

* Disable new SystemFonts test on Linux until fonts are installed on CI machines.

Commit migrated from https://github.com/dotnet/corefx/commit/0b16938f178f4cb509b7cdd0737037439b8b7d8c

src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj
src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.Unix.cs [moved from src/libraries/System.Drawing.Common/src/Unix/System.Drawing/SystemFonts.cs with 72% similarity]
src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.Windows.cs [new file with mode: 0644]
src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.cs
src/libraries/System.Drawing.Common/tests/SystemFontsTests.cs

index 9798a16..99cab23 100644 (file)
@@ -50,6 +50,7 @@
     <Compile Include="System\Drawing\Pen.cs" />
     <Compile Include="System\Drawing\Pens.cs" />
     <Compile Include="System\Drawing\RotateFlipType.cs" />
+    <Compile Include="System\Drawing\SystemFonts.cs" />
     <Compile Include="System\Drawing\SystemPens.cs" />
     <Compile Include="System\Drawing\Printing\PrinterUnit.cs" />
     <Compile Include="System\Drawing\Printing\PreviewPageInfo.cs" />
     <Compile Include="System\Drawing\Region.Windows.cs" />
     <Compile Include="System\Drawing\StringFormat.cs" />
     <Compile Include="System\Drawing\SystemColors.cs" />
-    <Compile Include="System\Drawing\SystemFonts.cs" />
+    <Compile Include="System\Drawing\SystemFonts.Windows.cs" />
     <Compile Include="System\Drawing\SystemIcons.cs" />
     <Compile Include="System\Drawing\ToolboxBitmapAttribute.cs" />
     <Compile Include="System\Drawing\Text\PrivateFontCollection.Windows.cs" />
   <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
     <!-- Unix-specific -->
     <Compile Include="System\Drawing\Graphics.Unix.cs" />
+    <Compile Include="System\Drawing\SystemFonts.Unix.cs" />
     <Compile Include="Unix\System.Drawing\Bitmap.cs" />
     <Compile Include="Unix\System.Drawing\BufferedGraphics.cs" />
     <Compile Include="Unix\System.Drawing\BufferedGraphicsManager.cs" />
     <Compile Include="Unix\System.Drawing\SRDescriptionAttribute.cs" />
     <Compile Include="Unix\System.Drawing\StringFormat.cs" />
     <Compile Include="Unix\System.Drawing\SystemColors.cs" />
-    <Compile Include="Unix\System.Drawing\SystemFonts.cs" />
     <Compile Include="Unix\System.Drawing\SystemIcons.cs" />
     <Compile Include="Unix\System.Drawing\ToolboxBitmapAttribute.cs" />
     <Compile Include="System\Drawing\Drawing2D\AdjustableArrowCap.Unix.cs" />
 namespace System.Drawing
 {
 
-    public sealed class SystemFonts
+    public static partial class SystemFonts
     {
-
-        static SystemFonts()
-        {
-        }
-
-        private SystemFonts()
-        {
-        }
-
-        public static Font GetFontByName(string systemFontName)
-        {
-            if (systemFontName == "CaptionFont")
-                return CaptionFont;
-
-            if (systemFontName == "DefaultFont")
-                return DefaultFont;
-
-            if (systemFontName == "DialogFont")
-                return DialogFont;
-
-            if (systemFontName == "IconTitleFont")
-                return IconTitleFont;
-
-            if (systemFontName == "MenuFont")
-                return MenuFont;
-
-            if (systemFontName == "MessageBoxFont")
-                return MessageBoxFont;
-
-            if (systemFontName == "SmallCaptionFont")
-                return SmallCaptionFont;
-
-            if (systemFontName == "StatusFont")
-                return StatusFont;
-
-            return null;
-        }
-
         public static Font CaptionFont
         {
             get { return new Font("Microsoft Sans Serif", 11, "CaptionFont"); }
diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/SystemFonts.Windows.cs
new file mode 100644 (file)
index 0000000..68238e0
--- /dev/null
@@ -0,0 +1,266 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace System.Drawing
+{
+    public static partial class SystemFonts
+    {
+        public static Font CaptionFont
+        {
+            get
+            {
+                Font captionFont = null;
+
+                var data = new NativeMethods.NONCLIENTMETRICS();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
+
+                if (result)
+                {
+                    captionFont = GetFontFromData(data.lfCaptionFont);
+                }
+
+                captionFont.SetSystemFontName(nameof(CaptionFont));
+                return captionFont;
+            }
+        }
+
+        public static Font SmallCaptionFont
+        {
+            get
+            {
+                Font smcaptionFont = null;
+
+                var data = new NativeMethods.NONCLIENTMETRICS();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
+
+                if (result)
+                {
+                    smcaptionFont = GetFontFromData(data.lfSmCaptionFont);
+                }
+
+                smcaptionFont.SetSystemFontName(nameof(SmallCaptionFont));
+                return smcaptionFont;
+            }
+        }
+
+        public static Font MenuFont
+        {
+            get
+            {
+                Font menuFont = null;
+
+                var data = new NativeMethods.NONCLIENTMETRICS();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
+
+                if (result)
+                {
+                    menuFont = GetFontFromData(data.lfMenuFont);
+                }
+
+                menuFont.SetSystemFontName(nameof(MenuFont));
+                return menuFont;
+            }
+        }
+
+        public static Font StatusFont
+        {
+            get
+            {
+                Font statusFont = null;
+
+                var data = new NativeMethods.NONCLIENTMETRICS();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
+
+                if (result)
+                {
+                    statusFont = GetFontFromData(data.lfStatusFont);
+                }
+
+                statusFont.SetSystemFontName(nameof(StatusFont));
+                return statusFont;
+            }
+        }
+
+        public static Font MessageBoxFont
+        {
+            get
+            {
+                Font messageBoxFont = null;
+
+                var data = new NativeMethods.NONCLIENTMETRICS();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
+
+                if (result)
+                {
+                    messageBoxFont = GetFontFromData(data.lfMessageFont);
+                }
+
+                messageBoxFont.SetSystemFontName(nameof(MessageBoxFont));
+                return messageBoxFont;
+            }
+        }
+
+        private static bool IsCriticalFontException(Exception ex)
+        {
+            return !(
+                // In any of these cases we'll handle the exception.
+                ex is ExternalException ||
+                ex is ArgumentException ||
+                ex is OutOfMemoryException || // GDI+ throws this one for many reasons other than actual OOM.
+                ex is InvalidOperationException ||
+                ex is NotImplementedException ||
+                ex is FileNotFoundException);
+        }
+
+        public static Font IconTitleFont
+        {
+            get
+            {
+                Font iconTitleFont = null;
+
+                var itfont = new SafeNativeMethods.LOGFONT();
+                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETICONTITLELOGFONT, Marshal.SizeOf(itfont), itfont, 0);
+
+                if (result)
+                {
+                    iconTitleFont = GetFontFromData(itfont);
+                }
+
+                iconTitleFont.SetSystemFontName(nameof(IconTitleFont));
+                return iconTitleFont;
+            }
+        }
+
+        public static Font DefaultFont
+        {
+            get
+            {
+                Font defaultFont = null;
+                
+                // For Arabic systems, always return Tahoma 8.
+                bool systemDefaultLCIDIsArabic = (UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0001;
+                if (systemDefaultLCIDIsArabic)
+                {
+                    try
+                    {
+                        defaultFont = new Font("Tahoma", 8);
+                    }
+                    catch (Exception ex) when (!IsCriticalFontException(ex)) { }
+                }
+    
+                // First try DEFAULT_GUI.
+                if (defaultFont == null)
+                {
+                    IntPtr handle = UnsafeNativeMethods.GetStockObject(NativeMethods.DEFAULT_GUI_FONT);
+                    try
+                    {
+                        using (Font fontInWorldUnits = Font.FromHfont(handle))
+                        {
+                            defaultFont = FontInPoints(fontInWorldUnits);
+                        }
+                    }
+                    catch (ArgumentException)
+                    {
+                    }
+                }
+
+                // If DEFAULT_GUI didn't work, try Tahoma.
+                if (defaultFont == null)
+                {
+                    try
+                    {
+                        defaultFont = new Font("Tahoma", 8);
+                    }
+                    catch (ArgumentException)
+                    {
+                    }
+                }
+
+                // Use GenericSansSerif as a last resort - this will always work.
+                if (defaultFont == null)
+                {
+                    defaultFont = new Font(FontFamily.GenericSansSerif, 8);
+                }
+
+                if (defaultFont.Unit != GraphicsUnit.Point)
+                {
+                    defaultFont = FontInPoints(defaultFont);
+                }
+
+                Debug.Assert(defaultFont != null, "defaultFont wasn't set.");
+
+                defaultFont.SetSystemFontName(nameof(DefaultFont));
+                return defaultFont;
+            }
+        }
+
+        public static Font DialogFont
+        {
+            get
+            {
+                Font dialogFont = null;
+
+                if ((UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0011)
+                {
+                    // Always return DefaultFont for Japanese cultures.
+                    dialogFont = DefaultFont;
+                }
+                else
+                {
+                    try
+                    {
+                        // Use MS Shell Dlg 2, 8pt for anything other than than Japanese.
+                        dialogFont = new Font("MS Shell Dlg 2", 8);
+                    }
+                    catch (ArgumentException)
+                    {
+                    }
+                }
+
+                if (dialogFont == null)
+                {
+                    dialogFont = DefaultFont;
+                }
+                else if (dialogFont.Unit != GraphicsUnit.Point)
+                {
+                    dialogFont = FontInPoints(dialogFont);
+                }
+
+                // For Japanese cultures, SystemFonts.DefaultFont returns a new Font object every time it is invoked.
+                // So for Japanese we return the DefaultFont with its SystemFontName set to DialogFont.
+                dialogFont.SetSystemFontName(nameof(DialogFont));
+                return dialogFont;
+            }
+        }
+
+        private static Font FontInPoints(Font font)
+        {
+            return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont);
+        }
+
+        private static Font GetFontFromData(SafeNativeMethods.LOGFONT logFont)
+        {
+            if (logFont == null)
+            {
+                return null;
+            }
+
+            Font font = null;
+            try
+            {
+                font = Font.FromLogFont(logFont);
+            }
+            catch (Exception ex) when (!IsCriticalFontException(ex)) { }
+
+            return
+                font == null ? DefaultFont :
+                font.Unit != GraphicsUnit.Point ? FontInPoints(font) :
+                font;
+        }
+    }
+}
index a4d417f..ed33d0b 100644 (file)
@@ -8,241 +8,8 @@ using System.Runtime.InteropServices;
 
 namespace System.Drawing
 {
-    public static class SystemFonts
+    public static partial class SystemFonts
     {
-        public static Font CaptionFont
-        {
-            get
-            {
-                Font captionFont = null;
-
-                var data = new NativeMethods.NONCLIENTMETRICS();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
-
-                if (result)
-                {
-                    captionFont = GetFontFromData(data.lfCaptionFont);
-                }
-
-                captionFont.SetSystemFontName(nameof(CaptionFont));
-                return captionFont;
-            }
-        }
-
-        public static Font SmallCaptionFont
-        {
-            get
-            {
-                Font smcaptionFont = null;
-
-                var data = new NativeMethods.NONCLIENTMETRICS();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
-
-                if (result)
-                {
-                    smcaptionFont = GetFontFromData(data.lfSmCaptionFont);
-                }
-
-                smcaptionFont.SetSystemFontName(nameof(SmallCaptionFont));
-                return smcaptionFont;
-            }
-        }
-
-        public static Font MenuFont
-        {
-            get
-            {
-                Font menuFont = null;
-
-                var data = new NativeMethods.NONCLIENTMETRICS();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
-
-                if (result)
-                {
-                    menuFont = GetFontFromData(data.lfMenuFont);
-                }
-
-                menuFont.SetSystemFontName(nameof(MenuFont));
-                return menuFont;
-            }
-        }
-
-        public static Font StatusFont
-        {
-            get
-            {
-                Font statusFont = null;
-
-                var data = new NativeMethods.NONCLIENTMETRICS();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
-
-                if (result)
-                {
-                    statusFont = GetFontFromData(data.lfStatusFont);
-                }
-
-                statusFont.SetSystemFontName(nameof(StatusFont));
-                return statusFont;
-            }
-        }
-
-        public static Font MessageBoxFont
-        {
-            get
-            {
-                Font messageBoxFont = null;
-
-                var data = new NativeMethods.NONCLIENTMETRICS();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
-
-                if (result)
-                {
-                    messageBoxFont = GetFontFromData(data.lfMessageFont);
-                }
-
-                messageBoxFont.SetSystemFontName(nameof(MessageBoxFont));
-                return messageBoxFont;
-            }
-        }
-
-        private static bool IsCriticalFontException(Exception ex)
-        {
-            return !(
-                // In any of these cases we'll handle the exception.
-                ex is ExternalException ||
-                ex is ArgumentException ||
-                ex is OutOfMemoryException || // GDI+ throws this one for many reasons other than actual OOM.
-                ex is InvalidOperationException ||
-                ex is NotImplementedException ||
-                ex is FileNotFoundException);
-        }
-
-        public static Font IconTitleFont
-        {
-            get
-            {
-                Font iconTitleFont = null;
-
-                var itfont = new SafeNativeMethods.LOGFONT();
-                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETICONTITLELOGFONT, Marshal.SizeOf(itfont), itfont, 0);
-
-                if (result)
-                {
-                    iconTitleFont = GetFontFromData(itfont);
-                }
-
-                iconTitleFont.SetSystemFontName(nameof(IconTitleFont));
-                return iconTitleFont;
-            }
-        }
-
-        public static Font DefaultFont
-        {
-            get
-            {
-                Font defaultFont = null;
-                
-                // For Arabic systems, always return Tahoma 8.
-                bool systemDefaultLCIDIsArabic = (UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0001;
-                if (systemDefaultLCIDIsArabic)
-                {
-                    try
-                    {
-                        defaultFont = new Font("Tahoma", 8);
-                    }
-                    catch (Exception ex) when (!IsCriticalFontException(ex)) { }
-                }
-    
-                // First try DEFAULT_GUI.
-                if (defaultFont == null)
-                {
-                    IntPtr handle = UnsafeNativeMethods.GetStockObject(NativeMethods.DEFAULT_GUI_FONT);
-                    try
-                    {
-                        using (Font fontInWorldUnits = Font.FromHfont(handle))
-                        {
-                            defaultFont = FontInPoints(fontInWorldUnits);
-                        }
-                    }
-                    catch (ArgumentException)
-                    {
-                    }
-                }
-
-                // If DEFAULT_GUI didn't work, try Tahoma.
-                if (defaultFont == null)
-                {
-                    try
-                    {
-                        defaultFont = new Font("Tahoma", 8);
-                    }
-                    catch (ArgumentException)
-                    {
-                    }
-                }
-
-                // Use GenericSansSerif as a last resort - this will always work.
-                if (defaultFont == null)
-                {
-                    defaultFont = new Font(FontFamily.GenericSansSerif, 8);
-                }
-
-                if (defaultFont.Unit != GraphicsUnit.Point)
-                {
-                    defaultFont = FontInPoints(defaultFont);
-                }
-
-                Debug.Assert(defaultFont != null, "defaultFont wasn't set.");
-
-                defaultFont.SetSystemFontName(nameof(DefaultFont));
-                return defaultFont;
-            }
-        }
-
-        public static Font DialogFont
-        {
-            get
-            {
-                Font dialogFont = null;
-
-                if ((UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0011)
-                {
-                    // Always return DefaultFont for Japanese cultures.
-                    dialogFont = DefaultFont;
-                }
-                else
-                {
-                    try
-                    {
-                        // Use MS Shell Dlg 2, 8pt for anything other than than Japanese.
-                        dialogFont = new Font("MS Shell Dlg 2", 8);
-                    }
-                    catch (ArgumentException)
-                    {
-                    }
-                }
-
-                if (dialogFont == null)
-                {
-                    dialogFont = DefaultFont;
-                }
-                else if (dialogFont.Unit != GraphicsUnit.Point)
-                {
-                    dialogFont = FontInPoints(dialogFont);
-                }
-
-                // For Japanese cultures, SystemFonts.DefaultFont returns a new Font object every time it is invoked.
-                // So for Japanese we return the DefaultFont with its SystemFontName set to DialogFont.
-                dialogFont.SetSystemFontName(nameof(DialogFont));
-                return dialogFont;
-            }
-        }
-
-        private static Font FontInPoints(Font font)
-        {
-            return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont);
-        }
-
         public static Font GetFontByName(string systemFontName)
         {
             if (nameof(CaptionFont).Equals(systemFontName))
@@ -280,25 +47,5 @@ namespace System.Drawing
 
             return null;
         }
-
-        private static Font GetFontFromData(SafeNativeMethods.LOGFONT logFont)
-        {
-            if (logFont == null)
-            {
-                return null;
-            }
-
-            Font font = null;
-            try
-            {
-                font = Font.FromLogFont(logFont);
-            }
-            catch (Exception ex) when (!IsCriticalFontException(ex)) { }
-
-            return
-                font == null ? DefaultFont :
-                font.Unit != GraphicsUnit.Point ? FontInPoints(font) :
-                font;
-        }
     }
 }
index 2ddc8b7..5e2029c 100644 (file)
@@ -11,6 +11,31 @@ namespace System.Drawing.Tests
     {
         public static IEnumerable<object[]> SystemFonts_TestData()
         {
+            yield return new object[] { (Func<Font>)(() => SystemFonts.CaptionFont) };
+            yield return new object[] { (Func<Font>)(() => SystemFonts.IconTitleFont) };
+            yield return new object[] { (Func<Font>)(() => SystemFonts.MenuFont) };
+            yield return new object[] { (Func<Font>)(() => SystemFonts.MessageBoxFont) };
+            yield return new object[] { (Func<Font>)(() => SystemFonts.SmallCaptionFont) };
+            yield return new object[] { (Func<Font>)(() => SystemFonts.StatusFont) };
+        }
+
+        [ActiveIssue(23690, TestPlatforms.Linux)]
+        [ConditionalTheory(Helpers.GdiplusIsAvailable)]
+        [MemberData(nameof(SystemFonts_TestData))]
+        public void SystemFont_Get_ReturnsExpected(Func<Font> getFont)
+        {
+            using (Font font = getFont())
+            using (Font otherFont = getFont())
+            {
+                Assert.NotNull(font);
+                Assert.NotNull(otherFont);
+                Assert.NotSame(font, otherFont);
+                Assert.Equal(font, otherFont);
+            }
+        }
+
+        public static IEnumerable<object[]> SystemFonts_WindowsNames_TestData()
+        {
             yield return Font(() => SystemFonts.CaptionFont, "CaptionFont", "Segoe UI");
             yield return Font(() => SystemFonts.IconTitleFont, "IconTitleFont", "Segoe UI");
             yield return Font(() => SystemFonts.MenuFont, "MenuFont", "Segoe UI");
@@ -27,10 +52,10 @@ namespace System.Drawing.Tests
 
         public static object[] Font(Func<Font> getFont, string systemFontName, string windowsFontName) => new object[] { getFont, systemFontName, windowsFontName };
 
-        [ActiveIssue(20884, TestPlatforms.AnyUnix)]
+        [PlatformSpecific(TestPlatforms.Windows)]
         [ConditionalTheory(Helpers.GdiplusIsAvailable)]
-        [MemberData(nameof(SystemFonts_TestData))]
-        public void SystemFont_Get_ReturnsExpected(Func<Font> getFont, string systemFontName, string windowsFontName)
+        [MemberData(nameof(SystemFonts_WindowsNames_TestData))]
+        public void SystemFont_Get_ReturnsExpected_WindowsNames(Func<Font> getFont, string systemFontName, string windowsFontName)
         {
             using (Font font = getFont())
             using (Font otherFont = getFont())