File: commonui\System\Drawing\SystemFonts.cs
Project: ndp\fx\src\System.Drawing.csproj (System.Drawing)
//------------------------------------------------------------------------------
// <copyright file="SystemFonts.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Drawing {
 
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.IO;
    using System.Runtime.Versioning;
    
    /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts"]/*' />
    /// <devdoc>
    ///     Selected system-wide fonts.
    /// </devdoc>
    public sealed class SystemFonts {
        private static readonly object SystemFontsKey = new object();
 
        // Cannot be instantiated.
        private SystemFonts() {
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.CaptionFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for captions.
        ///    </para>
        /// </devdoc>
        public static Font CaptionFont {
            get {
                Font captionFont = null;
 
                NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
 
                if (result && data.lfCaptionFont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        captionFont = Font.FromLogFont(data.lfCaptionFont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                        CodeAccessPermission.RevertAssert();
                    }
 
                    if( captionFont == null ) {
                        captionFont = DefaultFont;
                    } else if (captionFont.Unit != GraphicsUnit.Point) {
                        captionFont = FontInPoints(captionFont);
                    }
                }
 
                captionFont.SetSystemFontName("CaptionFont");
                return captionFont;
            }
        }
 
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.SmallCaptionFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for small captions.
        ///    </para>
        /// </devdoc>
        public static Font SmallCaptionFont {
            get {
                Font smcaptionFont = null;
 
                NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
 
                if (result && data.lfSmCaptionFont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        smcaptionFont = Font.FromLogFont(data.lfSmCaptionFont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                        CodeAccessPermission.RevertAssert();
                    }
 
                    if( smcaptionFont == null ) {
                        smcaptionFont = DefaultFont;
                    } else if (smcaptionFont.Unit != GraphicsUnit.Point) {
                        smcaptionFont = FontInPoints(smcaptionFont);
                    }
                }
 
                smcaptionFont.SetSystemFontName("SmallCaptionFont");
                return smcaptionFont;
            }
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.MenuFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for menus.
        ///    </para>
        /// </devdoc>
        public static Font MenuFont {
            get {
                Font menuFont = null;
 
                NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
 
                if (result && data.lfMenuFont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        menuFont = Font.FromLogFont(data.lfMenuFont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                        CodeAccessPermission.RevertAssert();
                    }
 
                    if( menuFont == null ) {
                        menuFont = DefaultFont;
                    } else if (menuFont.Unit != GraphicsUnit.Point) {
                        menuFont = FontInPoints(menuFont);
                    }
                }
 
                menuFont.SetSystemFontName("MenuFont");
                return menuFont;
            }
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.StatusFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for status bars.
        ///    </para>
        /// </devdoc>
        public static Font StatusFont {
            get {
                Font statusFont = null;
 
                NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
 
                if (result && data.lfStatusFont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        statusFont = Font.FromLogFont(data.lfStatusFont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                       CodeAccessPermission.RevertAssert();
                    }
 
                    if( statusFont == null ) {
                        statusFont = DefaultFont;
                    } else if (statusFont.Unit != GraphicsUnit.Point) {
                        statusFont = FontInPoints(statusFont);
                    }
                }
 
               statusFont.SetSystemFontName("StatusFont");
               return statusFont;
            }
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.MessageBoxFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for message boxes.
        ///    </para>
        /// </devdoc>
        public static Font MessageBoxFont {
            get {
                Font messageboxFont = null;
 
                NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0);
 
                if (result && data.lfMessageFont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        messageboxFont = Font.FromLogFont(data.lfMessageFont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                        CodeAccessPermission.RevertAssert();
                    }
 
                    if( messageboxFont == null ) {
                        messageboxFont = DefaultFont;
                    } else if (messageboxFont.Unit != GraphicsUnit.Point) {
                        messageboxFont = FontInPoints(messageboxFont);
                    }
                }
 
                messageboxFont.SetSystemFontName("MessageBoxFont");
                return messageboxFont;
            }
        }    
    
        /// <devdoc>
        ///     Determines whether the specified exception should be handled.
        /// </devdoc>
        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 );
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.IconTitleFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for icon titles.
        ///    </para>
        /// </devdoc>
        public static Font IconTitleFont {
            get {
                Font icontitleFont = null;
 
                SafeNativeMethods.LOGFONT itfont = new SafeNativeMethods.LOGFONT();
                bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETICONTITLELOGFONT, Marshal.SizeOf(itfont), itfont, 0);
 
                if (result && itfont != null) {
                    // SECREVIEW : This assert is ok.  The LOGFONT struct passed to Font.FromLogFont was obtained from the system.
                    //
                    IntSecurity.ObjectFromWin32Handle.Assert();
                    try {
                        icontitleFont = Font.FromLogFont(itfont);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                    finally {
                        CodeAccessPermission.RevertAssert();
                    }
                    
                    if (icontitleFont == null ) {
                        icontitleFont = DefaultFont;
                    } else if (icontitleFont.Unit != GraphicsUnit.Point) {
                        icontitleFont = FontInPoints(icontitleFont);
                    }
                }
 
                icontitleFont.SetSystemFontName("IconTitleFont");
                return icontitleFont;
            }
        }
 
        //////////////////////////////////////////////////////////////////////////////////////////////
        //                                                                                          //
        // SystemFonts.DefaultFont is code moved from System.Windows.Forms.Control.DefaultFont      //
        // System.Windows.Forms.Control.DefaultFont delegates to SystemFonts.DefaultFont now.       //
        // Treat any changes to this code as you would treat breaking changes.                      //
        //                                                                                          //
        //////////////////////////////////////////////////////////////////////////////////////////////
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.DefaultFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's default font.
        ///    </para>
        /// </devdoc>
        public static Font DefaultFont {
            get {
                Font defaultFont = null;
 
                //special case defaultfont for arabic systems too
                bool systemDefaultLCIDIsArabic = false;
 
                // For Japanese on Win9x get the MS UI Gothic font
                if (Environment.OSVersion.Platform == System.PlatformID.Win32NT &&
                    Environment.OSVersion.Version.Major <= 4) {
 
                    if((UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0011) {
                        try {
                            defaultFont = new Font("MS UI Gothic", 9);
                        }
                        //fall through here if this fails and we'll get the default
                        //font via the DEFAULT_GUI method
                        catch (Exception ex) {
                            if( IsCriticalFontException(ex) ){
                                throw;
                            }
                        }
                    }
                }
 
                if (defaultFont == null) {
                    systemDefaultLCIDIsArabic = ((UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0001);
                }
 
                // For arabic systems, regardless of the platform, always return Tahoma 8.
                // vsWhidbey 82453.
                if (systemDefaultLCIDIsArabic) {
                    Debug.Assert(defaultFont == null);
                    // Try TAHOMA 8
                    try {
                        defaultFont = new Font("Tahoma", 8);
                    }
                    catch (Exception ex) {
                        if( IsCriticalFontException(ex) ){
                            throw;
                        }
                    }
                }
 
                //
                // Neither Japanese on Win9x nor Arabic.
                // First try DEFAULT_GUI, then Tahoma 8, then GenericSansSerif 8.
                //
 
                // first, try DEFAULT_GUI font.
                //
                if (defaultFont == null) {
                    IntPtr handle = UnsafeNativeMethods.GetStockObject(NativeMethods.DEFAULT_GUI_FONT);        
                    try {
                        Font fontInWorldUnits = null;
 
                        // SECREVIEW : We know that we got the handle from the stock object,
                        //           : so this is always safe.
                        //
                        IntSecurity.ObjectFromWin32Handle.Assert();
                        try {
                            fontInWorldUnits = Font.FromHfont(handle);
                        }
                        finally {
                            CodeAccessPermission.RevertAssert();
                        }
 
                        try{
                            defaultFont = FontInPoints(fontInWorldUnits);
                        }
                        finally{
                            fontInWorldUnits.Dispose();
                        }
                    }
                    catch (ArgumentException) {
                    }
                }
 
                // If DEFAULT_GUI didn't work, we try Tahoma.
                //
                if (defaultFont == null) {
                    try {
                        defaultFont = new Font("Tahoma", 8);
                    }
                    catch (ArgumentException) {
                    }
                }
 
                // Last resort, we use the GenericSansSerif - 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("DefaultFont");
                return defaultFont;
            }
        }
 
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.DialogFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the system's font for dialogs.
        ///    </para>
        /// </devdoc>
        public static Font DialogFont {
            get {
                Font dialogFont = null;
 
                if((UnsafeNativeMethods.GetSystemDefaultLCID() & 0x3ff) == 0x0011) {
                    // for JAPANESE culture always return DefaultFont
                    dialogFont = DefaultFont;
                } else if (Environment.OSVersion.Platform == System.PlatformID.Win32Windows) {
                    // use DefaultFont for Win9X
                    dialogFont = DefaultFont;
                } else  {
                    try {
                        // use MS Shell Dlg 2, 8pt for anything else than Japanese and Win9x
                        dialogFont = new Font("MS Shell Dlg 2", 8);
                    }
                    catch (ArgumentException) {
                    }
                }
 
                if (dialogFont == null) {
                    dialogFont = DefaultFont;
                } else if (dialogFont.Unit != GraphicsUnit.Point) {
                    dialogFont = FontInPoints(dialogFont);
                }
 
                //
                // JAPANESE or Win9x: SystemFonts.DefaultFont returns a new Font object every time it is invoked.
                // So for JAPANESE or Win9x we return the DefaultFont w/ its SystemFontName set to DialogFont.
                //
                dialogFont.SetSystemFontName("DialogFont");
                return dialogFont;
            }
        }
 
        //Copied from System.Windows.Forms.ControlPaint
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        private static Font FontInPoints(Font font) {
            return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont);
        }
        /// <include file='doc\SystemFonts.uex' path='docs/doc[@for="SystemFonts.GetFontByName"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the <see cref='System.Drawing.SystemFont'/> that corresponds to a given SystemFont name.
        ///    </para>
        /// </devdoc>
        public static Font GetFontByName(string systemFontName) {
            if ("CaptionFont".Equals(systemFontName)) {
                return CaptionFont;
            } else if ("DefaultFont".Equals(systemFontName)) {
                return DefaultFont;
            } else if ("DialogFont".Equals(systemFontName)) {
                return DialogFont;
            } else if ("IconTitleFont".Equals(systemFontName)) {
                return IconTitleFont;
            } else if ("MenuFont".Equals(systemFontName)) {
                return MenuFont;
            } else if ("MessageBoxFont".Equals(systemFontName)) {
                return MessageBoxFont;
            } else if ("SmallCaptionFont".Equals(systemFontName)) {
                return SmallCaptionFont;
            } else if ("StatusFont".Equals(systemFontName)) {
                return StatusFont;
            } else {
                return null;
            }
        }
    }
}