2006-08-11 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System.Drawing / System.Drawing / Font.cs
index 31044a5b3067f096e548a432c9556fc4ad72e3c4..841fc226c04340469dbe40fc303ecf03e7684b91 100644 (file)
@@ -9,8 +9,7 @@
 //     Ravindra (rkumar@novell.com)
 //
 // Copyright (C) 2004 Ximian, Inc. (http://www.ximian.com)
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2006 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -32,9 +31,9 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Runtime.Serialization;
 using System.Runtime.InteropServices;
+using System.Security.Permissions;
 using System.ComponentModel;
 
 namespace System.Drawing
@@ -46,13 +45,55 @@ namespace System.Drawing
        public sealed class Font : MarshalByRefObject, ISerializable, ICloneable, IDisposable
        {
                private IntPtr  fontObject = IntPtr.Zero;
+               private string  systemFontName;
+               private float _size;
+
+               private const byte DefaultCharSet = 1;
+               private static int CharSetOffset = -1;
+               private static int FaceNameOffset = -1;
+
+               private void CreateFont (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
+               {
+#if ONLY_1_1
+                       if (familyName == null)
+                               throw new ArgumentNullException ("familyName");
+#endif
+                        FontFamily family;
+                       // NOTE: If family name is null, empty or invalid,
+                       // MS creates Microsoft Sans Serif font.
+                       try {
+                               family = new FontFamily (familyName);
+                       }
+                       catch (Exception){
+                               family = FontFamily.GenericSansSerif;
+                       }
+
+                       setProperties (family, emSize, style, unit, charSet, isVertical);           
+                       Status status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style, unit, out fontObject);
+                       GDIPlus.CheckStatus (status);
+               }
 
                        private Font (SerializationInfo info, StreamingContext context)
                {
+                       string          name;
+                       float           size;
+                       FontStyle       style;
+                       GraphicsUnit    unit;
+
+                       name = (string)info.GetValue("Name", typeof(string));
+                       size = (float)info.GetValue("Size", typeof(float));
+                       style = (FontStyle)info.GetValue("Style", typeof(FontStyle));
+                       unit = (GraphicsUnit)info.GetValue("Unit", typeof(GraphicsUnit));
+                       CreateFont(name, size, style, unit, DefaultCharSet, false);
                }
 
                void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
                {
+                       info.AddValue("Name", Name);
+                       info.AddValue("Size", Size);
+                       info.AddValue("Style", Style);
+                       info.AddValue("Unit", Unit);
                }
 
                ~Font()
@@ -63,11 +104,11 @@ namespace System.Drawing
                public void Dispose ()
                {
                        if (fontObject != IntPtr.Zero) {
-                               lock (this)
-                               {
-                                       GDIPlus.CheckStatus (GDIPlus.GdipDeleteFont (fontObject));
-                               }
+                               Status status = GDIPlus.GdipDeleteFont (fontObject);
+                               fontObject = IntPtr.Zero;
                                GC.SuppressFinalize (this);
+                               // check the status code (throw) at the last step
+                               GDIPlus.CheckStatus (status);
                        }
                }
 
@@ -128,7 +169,7 @@ namespace System.Drawing
                internal void setProperties (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
                {
                        _name = family.Name;
-                       _fontFamily = family;
+                       _fontFamily = family;                   
                        _size = emSize;
 
                        // MS throws ArgumentException, if unit is set to GraphicsUnit.Display
@@ -158,11 +199,10 @@ namespace System.Drawing
                {
                        OperatingSystem osInfo = Environment.OSVersion;
                        IntPtr                  newObject;
-                       IntPtr                  hdc;
-                       IntPtr                  oldFont;
+                       IntPtr                  hdc;                    
                        FontStyle               newStyle = FontStyle.Regular;
                        float                   newSize;
-                       LOGFONTA                lf = new LOGFONTA ();
+                       LOGFONT                 lf = new LOGFONT ();
 
                        // Sanity. Should we throw an exception?
                        if (Hfont == IntPtr.Zero) {
@@ -170,15 +210,11 @@ namespace System.Drawing
                                return(result);
                        }
 
-                       if ((int) osInfo.Platform == 128) {
-                       // If we're on Unix we use our private gdiplus API to avoid Wine 
-                       // dependencies in S.D
-
-                               lock (typeof (Font))
-                               {
-                                       Status s = GDIPlus.GdipCreateFontFromHfont (Hfont, out newObject, ref lf);
-                                       GDIPlus.CheckStatus (s);
-                               }
+                       if ((int) osInfo.Platform == 128 || (int) osInfo.Platform == 4) {
+                               // If we're on Unix we use our private gdiplus API to avoid Wine 
+                               // dependencies in S.D
+                               Status s = GDIPlus.GdipCreateFontFromHfont (Hfont, out newObject, ref lf);
+                               GDIPlus.CheckStatus (s);
                        } else {
 
                                // This needs testing
@@ -187,17 +223,12 @@ namespace System.Drawing
 
                                newStyle = FontStyle.Regular;
 
-                               lock (typeof (Font))
-                               {
-                                       hdc = GDIPlus.GetDC (IntPtr.Zero);
-                                       oldFont = GDIPlus.SelectObject (hdc, Hfont);
-                                       GDIPlus.CheckStatus (GDIPlus.GdipCreateFontFromDC (hdc, out newObject));
-                                       GDIPlus.CheckStatus (GDIPlus.GdipGetLogFontA (newObject, IntPtr.Zero, ref lf));
-                                       GDIPlus.SelectObject (hdc, oldFont);
-                                       GDIPlus.ReleaseDC (hdc);
-                               }
+                               hdc = GDIPlus.GetDC (IntPtr.Zero);
+                               Font f = FromLogFont (lf, hdc);
+                               GDIPlus.ReleaseDC (hdc);
+                               return f;                               
                        }
-
+                       
                        if (lf.lfItalic != 0) {
                                newStyle |= FontStyle.Italic;
                        }
@@ -225,69 +256,68 @@ namespace System.Drawing
 
                public IntPtr ToHfont ()
                {
+                       if (fontObject == IntPtr.Zero)
+                               throw new ArgumentException (Locale.GetText ("Object has been disposed."));
+
                        IntPtr Hfont;
                        OperatingSystem osInfo = Environment.OSVersion;
-
-                       // Sanity. Should we throw an exception?
-                       if (fontObject == IntPtr.Zero) {
-                               return IntPtr.Zero;
-                       }
-
-                       if ((int) osInfo.Platform == 128) {
-                               // If we're on Unix we use our private gdiplus API
-                               GDIPlus.CheckStatus (GDIPlus.GdipGetHfont (fontObject, out Hfont));
+                       if ((int) osInfo.Platform == 128 || (int) osInfo.Platform == 4) {
+                               return fontObject;
                        } else {
-                               // This needs testing, but I don't have a working win32 mono
-                               // environment. 
-                               LOGFONTA lf = new LOGFONTA ();
-
-                               GDIPlus.CheckStatus (GDIPlus.GdipGetLogFontA (fontObject, IntPtr.Zero, ref lf));
-                               Hfont = GDIPlus.CreateFontIndirectA (ref lf);
+                               object olf = new LOGFONT ();
+                               ToLogFont(olf);
+                               LOGFONT lf = (LOGFONT)olf;
+                               Hfont = GDIPlus.CreateFontIndirect (ref lf);
                        }
                        return Hfont;
                }
 
                internal Font (IntPtr newFontObject, string familyName, FontStyle style, float size)
                {
-                       FontFamily fontFamily = new FontFamily (familyName);
+                       FontFamily fontFamily;                  
+                       
+                       try {
+                               fontFamily = new FontFamily (familyName);
+                       }
+                       catch (Exception){
+                               fontFamily = FontFamily.GenericSansSerif;
+                       }
+                       
                        setProperties (fontFamily, size, style, GraphicsUnit.Pixel, 0, false);
                        fontObject = newFontObject;
                }
 
                public Font (Font original, FontStyle style)
                {
-                       lock (this)
-                       {
-                               Status status;
-                               setProperties (original.FontFamily, original.Size, style, original.Unit, original.GdiCharSet, original.GdiVerticalFont);
+                       Status status;
+                       setProperties (original.FontFamily, original.Size, style, original.Unit, original.GdiCharSet, original.GdiVerticalFont);
                                
-                               status = GDIPlus.GdipCreateFont (_fontFamily.NativeObject,      Size,  Style,   Unit,  out fontObject);
-                               GDIPlus.CheckStatus (status);
-                       }
+                       status = GDIPlus.GdipCreateFont (_fontFamily.NativeObject,      Size,  Style,   Unit,  out fontObject);
+                       GDIPlus.CheckStatus (status);                   
                }
 
                public Font (FontFamily family, float emSize,  GraphicsUnit unit)
-                       : this (family, emSize, FontStyle.Regular, unit, (byte)0, false)
+                       : this (family, emSize, FontStyle.Regular, unit, DefaultCharSet, false)
                {
                }
 
                public Font (string familyName, float emSize,  GraphicsUnit unit)
-                       : this (new FontFamily (familyName), emSize, FontStyle.Regular, unit, (byte)0, false)
+                       : this (new FontFamily (familyName), emSize, FontStyle.Regular, unit, DefaultCharSet, false)
                {
                }
 
                public Font (FontFamily family, float emSize)
-                       : this (family, emSize, FontStyle.Regular, GraphicsUnit.Point, (byte)0, false)
+                       : this (family, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
                {
                }
 
                public Font (FontFamily family, float emSize, FontStyle style)
-                       : this (family, emSize, style, GraphicsUnit.Point, (byte)0, false)
+                       : this (family, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
                {
                }
 
                public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
-                       : this (family, emSize, style, unit, (byte)0, false)
+                       : this (family, emSize, style, unit, DefaultCharSet, false)
                {
                }
 
@@ -299,28 +329,27 @@ namespace System.Drawing
                public Font (FontFamily family, float emSize, FontStyle style,
                                GraphicsUnit unit, byte charSet, bool isVertical)
                {
-                       // MS does not accept null family
-                       lock (this)
-                       {
-                               Status status;
-                               setProperties (family, emSize, style, unit, charSet, isVertical);               
-                               status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style,   unit,  out fontObject);
-                               GDIPlus.CheckStatus (status);
-                       }
+                       if (family == null)
+                               throw new ArgumentNullException ("family");
+
+                       Status status;
+                       setProperties (family, emSize, style, unit, charSet, isVertical);               
+                       status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style,   unit,  out fontObject);
+                       GDIPlus.CheckStatus (status);
                }
 
                public Font (string familyName, float emSize)
-                       : this (familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, (byte)0, false)
+                       : this (familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
                {
                }
 
                public Font (string familyName, float emSize, FontStyle style)
-                       : this (familyName, emSize, style, GraphicsUnit.Point, (byte)0, false)
+                       : this (familyName, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
                {
                }
 
                public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit)
-                       : this (familyName, emSize, style, unit, (byte)0, false)
+                       : this (familyName, emSize, style, unit, DefaultCharSet, false)
                {
                }
 
@@ -332,17 +361,7 @@ namespace System.Drawing
                public Font (string familyName, float emSize, FontStyle style,
                                GraphicsUnit unit, byte charSet, bool isVertical)
                {
-                       lock (this)
-                       {
-                               // NOTE: If family name is null, empty or invalid,
-                               // MS creates Microsoft Sans Serif font.
-                               Status status;
-                               FontFamily family = new FontFamily (familyName);
-                               setProperties (family, emSize, style, unit, charSet, isVertical);
-                               
-                               status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style, unit, out fontObject);
-                               GDIPlus.CheckStatus (status);
-                       }
+                       CreateFont(familyName, emSize, style, unit, charSet, isVertical);
                }
 
                public object Clone ()
@@ -352,12 +371,17 @@ namespace System.Drawing
 
                internal IntPtr NativeObject {            
                        get {
-                                       return fontObject;
+                               return fontObject;
                        }
+               }
+
+#if NET_2_0
+               internal string SysFontName {
                        set {
-                                       fontObject = value;
+                               systemFontName = value;
                        }
                }
+#endif
 
                private bool _bold;
 
@@ -395,14 +419,24 @@ namespace System.Drawing
                        }
                }
 
-               private int _height;
-
                [Browsable (false)]
                public int Height {
                        get {
-                               return _height;
+                               return (int) Math.Ceiling (GetHeight ());
+                       }
+               }
+
+#if NET_2_0
+               [Browsable(false)]
+               public bool IsSystemFont {
+                       get {
+                               if (systemFontName == null)
+                                       return false;
+
+                               return StringComparer.InvariantCulture.Compare (systemFontName, string.Empty) != 0;
                        }
                }
+#endif
 
                private bool _italic;
 
@@ -423,11 +457,10 @@ namespace System.Drawing
                                return _name;
                        }
                }
-
-               private float _size;
+               
                public float Size {
                        get {
-                               return _size;
+                               return _size;                   
                        }
                }
 
@@ -458,6 +491,14 @@ namespace System.Drawing
                        }
                }
 
+#if NET_2_0
+               [Browsable(false)]
+               public string SystemFontName {
+                       get {
+                               return systemFontName;
+                       }
+               }
+#endif
                private bool _underline;
 
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
@@ -478,12 +519,11 @@ namespace System.Drawing
 
                public override bool Equals (object obj)
                {
-                       if (! (obj is Font))
+                       Font fnt = (obj as Font);
+                       if (fnt == null)
                                return false;
-                               
-                       Font fnt = (Font) obj;
-                       
-                       if (fnt.FontFamily == FontFamily && fnt.Size == Size &&
+
+                       if (fnt.FontFamily.Equals (FontFamily) && fnt.Size == Size &&
                            fnt.Style == Style && fnt.Unit == Unit &&
                            fnt.GdiCharSet == GdiCharSet && 
                            fnt.GdiVerticalFont == GdiVerticalFont)
@@ -503,53 +543,176 @@ namespace System.Drawing
                        throw new NotImplementedException ();
                }
 
-               [MonoTODO]
+               [MonoTODO("This is temporary implementation")]
                public static Font FromLogFont (object lf,  IntPtr hdc)
                {
-                       throw new NotImplementedException ();
+                       IntPtr newObject;
+                       LOGFONT o = (LOGFONT)lf;
+                       Status status = GDIPlus.GdipCreateFontFromLogfont (hdc, ref o, out newObject);
+                       GDIPlus.CheckStatus (status);
+                       return new Font (newObject, "Microsoft Sans Serif", FontStyle.Regular, 10);
                }
 
                public float GetHeight ()
                {
-                       return (float) _height;
+                       return GetHeight (Graphics.systemDpiY);
                }
 
-               [MonoTODO]
                public static Font FromLogFont (object lf)
                {
-                       throw new NotImplementedException ();
+                       if ((int) Environment.OSVersion.Platform == 128 || (int) Environment.OSVersion.Platform == 4) {
+                               return FromLogFont(lf, IntPtr.Zero);
+                       } else {
+                               IntPtr  hDC;
+
+                               hDC = IntPtr.Zero;
+
+                               try {
+                                       hDC = GDIPlus.GetDC(IntPtr.Zero);
+                                       return FromLogFont (lf, hDC);
+                               }
+
+                               finally {
+                                       GDIPlus.ReleaseDC(hDC);
+                               }
+                       }
+
                }
 
-               [MonoTODO]
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public void ToLogFont (object logFont)
                {
-                       throw new NotImplementedException ();
+                       Graphics g;
+
+                       g = null;
+
+                       if ((int) Environment.OSVersion.Platform == 128 || (int) Environment.OSVersion.Platform == 4) {
+                               // Unix
+                               Bitmap  img;
+
+                               img = null;
+
+                               try {
+                                       // We don't have a window we could associate the DC with
+                                       // so we use an image instead
+                                       img = new Bitmap(1, 1, Imaging.PixelFormat.Format32bppArgb);
+                                       g = Graphics.FromImage(img);
+                                       ToLogFont(logFont, g);
+                               }
+
+                               finally {
+                                       if (g != null) {
+                                               g.Dispose();
+                                       }
+
+                                       if (img != null) {
+                                               img.Dispose();
+                                       }
+                               }
+                       } else {
+                               // Windows
+                               IntPtr  hDC;
+
+                               hDC = IntPtr.Zero;
+
+                               try {
+
+                                       hDC = GDIPlus.GetDC(IntPtr.Zero);
+                                       g = Graphics.FromHdc(hDC);
+
+                                       ToLogFont (logFont, g);
+                               }
+
+                               finally {
+                                       if (g != null) {
+                                               g.Dispose();
+                                       }
+
+                                       GDIPlus.ReleaseDC(hDC);
+                               }
+                       }
                }
 
-               [MonoTODO]
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public void ToLogFont (object logFont, Graphics graphics)
                {
-                       throw new NotImplementedException ();
+                       if (graphics == null)
+                               throw new ArgumentNullException ("graphics");
+
+                       if (logFont == null) {
+#if NET_2_0
+                               throw new AccessViolationException ("logFont");
+#else
+                               throw new NullReferenceException ("logFont");
+#endif
+                       }
+
+                       Type st = logFont.GetType ();
+                       if (!st.IsLayoutSequential)
+                               throw new ArgumentException ("logFont", Locale.GetText ("Layout must be sequential."));
+
+                       // note: there is no exception if 'logFont' isn't big enough
+                       Type lf = typeof (LOGFONT);
+                       int size = Marshal.SizeOf (lf);
+                       if (Marshal.SizeOf (logFont) >= size) {
+                               Status status;
+                               IntPtr copy = Marshal.AllocHGlobal (size);
+                               try {
+                                       Marshal.StructureToPtr (logFont, copy, false);
+
+                                       status = GDIPlus.GdipGetLogFont (NativeObject, graphics.NativeObject, logFont);
+                                       if (status != Status.Ok) {
+                                               // reset to original values
+                                               Marshal.PtrToStructure (copy, logFont);
+                                       }
+                               }
+                               finally {
+                                       Marshal.FreeHGlobal (copy);
+                               }
+
+                               if (CharSetOffset == -1) {
+                                       CharSetOffset = (int) Marshal.OffsetOf (lf, "lfCharSet");
+                                       FaceNameOffset = (int) Marshal.OffsetOf (lf, "lfFaceName");
+                               }
+
+                               // note: Marshal.WriteByte(object,*) methods are unimplemented on Mono
+                               GCHandle gch = GCHandle.Alloc (logFont, GCHandleType.Pinned);
+                               try {
+                                       IntPtr ptr = gch.AddrOfPinnedObject ();
+                                       // if GDI+ lfCharSet is 0, then we return (S.D.) 1, otherwise the value is unchanged
+                                       if (Marshal.ReadByte (ptr, CharSetOffset) == 0) {
+                                               // set lfCharSet to 1 
+                                               Marshal.WriteByte (ptr, CharSetOffset, 1);
+                                       }
+                               }
+                               finally {
+                                       gch.Free ();
+                               }
+
+                               // now we can throw, if required
+                               GDIPlus.CheckStatus (status);
+                       }
                }
 
-               [MonoTODO]
                public float GetHeight (Graphics graphics)
                {
-                       if (Unit == GraphicsUnit.Pixel || Unit == GraphicsUnit.World)
-                               return GetHeight ();
-
-                       throw new NotImplementedException ();
+                       float size;
+                       Status status = GDIPlus.GdipGetFontHeight (fontObject, graphics.NativeObject, out size);
+                       GDIPlus.CheckStatus (status);
+                       return size;
                }
 
-               [MonoTODO]
                public float GetHeight (float dpi)
                {
-                       return GetHeight ();
+                       float size;
+                       Status status = GDIPlus.GdipGetFontHeightGivenDPI (fontObject, dpi, out size);
+                       GDIPlus.CheckStatus (status);
+                       return size;
                }
 
                public override String ToString ()
                {
-                       return String.Format ("[Font: Name={0}, Size={1}, Style={2}, Units={3}, GdiCharSet={4}, GdiVerticalFont={5}]", _name, _size, _style, _unit, _gdiCharSet, _gdiVerticalFont);
+                       return String.Format ("[Font: Name={0}, Size={1}, Units={2}, GdiCharSet={3}, GdiVerticalFont={4}]", _name, Size, (int)_unit, _gdiCharSet, _gdiVerticalFont);
                }
        }
 }