2 // System.Drawing.Fonts.cs
5 // Alexandre Pigolkine (pigolkine@gmx.de)
6 // Miguel de Icaza (miguel@ximian.com)
7 // Todd Berman (tberman@sevenl.com)
8 // Jordi Mas i Hernandez (jordi@ximian.com)
9 // Ravindra (rkumar@novell.com)
11 // Copyright (C) 2004 Ximian, Inc. (http://www.ximian.com)
12 // Copyright (C) 2004, 2006 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Runtime
.Serialization
;
35 using System
.Runtime
.InteropServices
;
36 using System
.Security
.Permissions
;
37 using System
.ComponentModel
;
39 namespace System
.Drawing
43 [Editor ("System.Drawing.Design.FontEditor, " + Consts
.AssemblySystem_Drawing_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
44 [TypeConverter (typeof (FontConverter
))]
45 public sealed class Font
: MarshalByRefObject
, ISerializable
, ICloneable
, IDisposable
47 private IntPtr fontObject
= IntPtr
.Zero
;
49 private string systemFontName
;
50 private string originalFontName
;
55 private const byte DefaultCharSet
= 1;
56 private static int CharSetOffset
= -1;
58 private void CreateFont (string familyName
, float emSize
, FontStyle style
, GraphicsUnit unit
, byte charSet
, bool isVertical
)
61 if (familyName
== null)
62 throw new ArgumentNullException ("familyName");
65 originalFontName
= familyName
;
68 // NOTE: If family name is null, empty or invalid,
69 // MS creates Microsoft Sans Serif font.
71 family
= new FontFamily (familyName
);
74 family
= FontFamily
.GenericSansSerif
;
77 setProperties (family
, emSize
, style
, unit
, charSet
, isVertical
);
78 Status status
= GDIPlus
.GdipCreateFont (family
.NativeObject
, emSize
, style
, unit
, out fontObject
);
80 if (status
== Status
.FontStyleNotFound
)
81 throw new ArgumentException (Locale
.GetText ("Style {0} isn't supported by font {1}.", style
.ToString (), familyName
));
83 GDIPlus
.CheckStatus (status
);
86 private Font (SerializationInfo info
, StreamingContext context
)
93 name
= (string)info
.GetValue("Name", typeof(string));
94 size
= (float)info
.GetValue("Size", typeof(float));
95 style
= (FontStyle
)info
.GetValue("Style", typeof(FontStyle
));
96 unit
= (GraphicsUnit
)info
.GetValue("Unit", typeof(GraphicsUnit
));
98 CreateFont(name
, size
, style
, unit
, DefaultCharSet
, false);
101 void ISerializable
.GetObjectData(SerializationInfo si
, StreamingContext context
)
103 si
.AddValue("Name", Name
);
104 si
.AddValue ("Size", Size
);
105 si
.AddValue ("Style", Style
);
106 si
.AddValue ("Unit", Unit
);
114 public void Dispose ()
116 if (fontObject
!= IntPtr
.Zero
) {
117 Status status
= GDIPlus
.GdipDeleteFont (fontObject
);
118 fontObject
= IntPtr
.Zero
;
119 GC
.SuppressFinalize (this);
120 // check the status code (throw) at the last step
121 GDIPlus
.CheckStatus (status
);
125 internal void unitConversion (GraphicsUnit fromUnit
, GraphicsUnit toUnit
, float nSrc
, out float nTrg
)
131 case GraphicsUnit
.Display
:
134 case GraphicsUnit
.Document
:
137 case GraphicsUnit
.Inch
:
140 case GraphicsUnit
.Millimeter
:
141 inchs
= nSrc
/ 25.4f
;
143 case GraphicsUnit
.Pixel
:
144 case GraphicsUnit
.World
:
145 inchs
= nSrc
/ Graphics
.systemDpiX
;
147 case GraphicsUnit
.Point
:
151 throw new ArgumentException("Invalid GraphicsUnit");
155 case GraphicsUnit
.Display
:
158 case GraphicsUnit
.Document
:
161 case GraphicsUnit
.Inch
:
164 case GraphicsUnit
.Millimeter
:
165 nTrg
= inchs
* 25.4f
;
167 case GraphicsUnit
.Pixel
:
168 case GraphicsUnit
.World
:
169 nTrg
= inchs
* Graphics
.systemDpiX
;
171 case GraphicsUnit
.Point
:
175 throw new ArgumentException("Invalid GraphicsUnit");
179 internal void setProperties (FontFamily family
, float emSize
, FontStyle style
, GraphicsUnit unit
, byte charSet
, bool isVertical
)
182 _fontFamily
= family
;
185 // MS throws ArgumentException, if unit is set to GraphicsUnit.Display
188 _gdiCharSet
= charSet
;
189 _gdiVerticalFont
= isVertical
;
191 unitConversion (unit
, GraphicsUnit
.Point
, emSize
, out _sizeInPoints
);
193 _bold
= _italic
= _strikeout
= _underline
= false;
195 if ((style
& FontStyle
.Bold
) == FontStyle
.Bold
)
198 if ((style
& FontStyle
.Italic
) == FontStyle
.Italic
)
201 if ((style
& FontStyle
.Strikeout
) == FontStyle
.Strikeout
)
204 if ((style
& FontStyle
.Underline
) == FontStyle
.Underline
)
208 public static Font
FromHfont (IntPtr hfont
)
212 FontStyle newStyle
= FontStyle
.Regular
;
214 LOGFONT lf
= new LOGFONT ();
216 // Sanity. Should we throw an exception?
217 if (hfont
== IntPtr
.Zero
) {
218 Font result
= new Font ("Arial", (float)10.0, FontStyle
.Regular
);
222 if (GDIPlus
.RunningOnUnix ()) {
223 // If we're on Unix we use our private gdiplus API to avoid Wine
224 // dependencies in S.D
225 Status s
= GDIPlus
.GdipCreateFontFromHfont (hfont
, out newObject
, ref lf
);
226 GDIPlus
.CheckStatus (s
);
229 // This needs testing
230 // GetDC, SelectObject, ReleaseDC GetTextMetric and
231 // GetFontFace are not really GDIPlus, see gdipFunctions.cs
233 newStyle
= FontStyle
.Regular
;
235 hdc
= GDIPlus
.GetDC (IntPtr
.Zero
);
237 return FromLogFont (lf
, hdc
);
240 GDIPlus
.ReleaseDC (IntPtr
.Zero
, hdc
);
244 if (lf
.lfItalic
!= 0) {
245 newStyle
|= FontStyle
.Italic
;
248 if (lf
.lfUnderline
!= 0) {
249 newStyle
|= FontStyle
.Underline
;
252 if (lf
.lfStrikeOut
!= 0) {
253 newStyle
|= FontStyle
.Strikeout
;
256 if (lf
.lfWeight
> 400) {
257 newStyle
|= FontStyle
.Bold
;
260 if (lf
.lfHeight
< 0) {
261 newSize
= lf
.lfHeight
* -1;
263 newSize
= lf
.lfHeight
;
266 return (new Font (newObject
, lf
.lfFaceName
, newStyle
, newSize
));
269 public IntPtr
ToHfont ()
271 if (fontObject
== IntPtr
.Zero
)
272 throw new ArgumentException (Locale
.GetText ("Object has been disposed."));
274 if (GDIPlus
.RunningOnUnix ())
277 // win32 specific code
279 olf
= new LOGFONT ();
282 LOGFONT lf
= (LOGFONT
)olf
;
283 return GDIPlus
.CreateFontIndirect (ref lf
);
286 internal Font (IntPtr newFontObject
, string familyName
, FontStyle style
, float size
)
288 FontFamily fontFamily
;
291 fontFamily
= new FontFamily (familyName
);
294 fontFamily
= FontFamily
.GenericSansSerif
;
297 setProperties (fontFamily
, size
, style
, GraphicsUnit
.Pixel
, 0, false);
298 fontObject
= newFontObject
;
301 public Font (Font prototype
, FontStyle newStyle
)
303 // no null checks, MS throws a NullReferenceException if original is null
304 setProperties (prototype
.FontFamily
, prototype
.Size
, newStyle
, prototype
.Unit
, prototype
.GdiCharSet
, prototype
.GdiVerticalFont
);
306 Status status
= GDIPlus
.GdipCreateFont (_fontFamily
.NativeObject
, Size
, Style
, Unit
, out fontObject
);
307 GDIPlus
.CheckStatus (status
);
310 public Font (FontFamily family
, float emSize
, GraphicsUnit unit
)
311 : this (family
, emSize
, FontStyle
.Regular
, unit
, DefaultCharSet
, false)
315 public Font (string familyName
, float emSize
, GraphicsUnit unit
)
316 : this (new FontFamily (familyName
), emSize
, FontStyle
.Regular
, unit
, DefaultCharSet
, false)
320 public Font (FontFamily family
, float emSize
)
321 : this (family
, emSize
, FontStyle
.Regular
, GraphicsUnit
.Point
, DefaultCharSet
, false)
325 public Font (FontFamily family
, float emSize
, FontStyle style
)
326 : this (family
, emSize
, style
, GraphicsUnit
.Point
, DefaultCharSet
, false)
330 public Font (FontFamily family
, float emSize
, FontStyle style
, GraphicsUnit unit
)
331 : this (family
, emSize
, style
, unit
, DefaultCharSet
, false)
335 public Font (FontFamily family
, float emSize
, FontStyle style
, GraphicsUnit unit
, byte gdiCharSet
)
336 : this (family
, emSize
, style
, unit
, gdiCharSet
, false)
340 public Font (FontFamily family
, float emSize
, FontStyle style
,
341 GraphicsUnit unit
, byte gdiCharSet
, bool gdiVerticalFont
)
344 throw new ArgumentNullException ("family");
347 setProperties (family
, emSize
, style
, unit
, gdiCharSet
, gdiVerticalFont
);
348 status
= GDIPlus
.GdipCreateFont (family
.NativeObject
, emSize
, style
, unit
, out fontObject
);
349 GDIPlus
.CheckStatus (status
);
352 public Font (string familyName
, float emSize
)
353 : this (familyName
, emSize
, FontStyle
.Regular
, GraphicsUnit
.Point
, DefaultCharSet
, false)
357 public Font (string familyName
, float emSize
, FontStyle style
)
358 : this (familyName
, emSize
, style
, GraphicsUnit
.Point
, DefaultCharSet
, false)
362 public Font (string familyName
, float emSize
, FontStyle style
, GraphicsUnit unit
)
363 : this (familyName
, emSize
, style
, unit
, DefaultCharSet
, false)
367 public Font (string familyName
, float emSize
, FontStyle style
, GraphicsUnit unit
, byte gdiCharSet
)
368 : this (familyName
, emSize
, style
, unit
, gdiCharSet
, false)
372 public Font (string familyName
, float emSize
, FontStyle style
,
373 GraphicsUnit unit
, byte gdiCharSet
, bool gdiVerticalFont
)
375 CreateFont (familyName
, emSize
, style
, unit
, gdiCharSet
, gdiVerticalFont
);
378 internal Font (string familyName
, float emSize
, string systemName
)
379 : this (familyName
, emSize
, FontStyle
.Regular
, GraphicsUnit
.Point
, DefaultCharSet
, false)
381 systemFontName
= systemName
;
384 public object Clone ()
386 return new Font (this, Style
);
389 internal IntPtr NativeObject
{
397 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
404 private FontFamily _fontFamily
;
407 public FontFamily FontFamily
{
413 private byte _gdiCharSet
;
415 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
416 public byte GdiCharSet
{
422 private bool _gdiVerticalFont
;
424 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
425 public bool GdiVerticalFont
{
427 return _gdiVerticalFont
;
434 return (int) Math
.Ceiling (GetHeight ());
440 public bool IsSystemFont
{
442 if (systemFontName
== null)
445 return StringComparer
.InvariantCulture
.Compare (systemFontName
, string.Empty
) != 0;
450 private bool _italic
;
452 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
459 private string _name
;
461 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
462 [Editor ("System.Drawing.Design.FontNameEditor, " + Consts
.AssemblySystem_Drawing_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
463 [TypeConverter (typeof (FontConverter
.FontNameConverter
))]
476 private float _sizeInPoints
;
479 public float SizeInPoints
{
481 return _sizeInPoints
;
485 private bool _strikeout
;
487 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
488 public bool Strikeout
{
494 private FontStyle _style
;
497 public FontStyle Style
{
505 public string SystemFontName
{
507 return systemFontName
;
512 public string OriginalFontName
{
514 return originalFontName
;
518 private bool _underline
;
520 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
521 public bool Underline
{
527 private GraphicsUnit _unit
;
529 [TypeConverter (typeof (FontConverter
.FontUnitConverter
))]
530 public GraphicsUnit Unit
{
536 public override bool Equals (object obj
)
538 Font fnt
= (obj
as Font
);
542 if (fnt
.FontFamily
.Equals (FontFamily
) && fnt
.Size
== Size
&&
543 fnt
.Style
== Style
&& fnt
.Unit
== Unit
&&
544 fnt
.GdiCharSet
== GdiCharSet
&&
545 fnt
.GdiVerticalFont
== GdiVerticalFont
)
551 public override int GetHashCode ()
553 return _name
.GetHashCode () ^ FontFamily
.GetHashCode () ^ _size
.GetHashCode () ^ _style
.GetHashCode () ^
554 _gdiCharSet ^ _gdiVerticalFont
.GetHashCode ();
557 [MonoTODO ("The hdc parameter has no direct equivalent in libgdiplus.")]
558 public static Font
FromHdc (IntPtr hdc
)
560 throw new NotImplementedException ();
563 [MonoTODO ("The returned font may not have all it's properties initialized correctly.")]
564 public static Font
FromLogFont (object lf
, IntPtr hdc
)
567 LOGFONT o
= (LOGFONT
)lf
;
568 Status status
= GDIPlus
.GdipCreateFontFromLogfont (hdc
, ref o
, out newObject
);
569 GDIPlus
.CheckStatus (status
);
570 return new Font (newObject
, "Microsoft Sans Serif", FontStyle
.Regular
, 10);
573 public float GetHeight ()
575 return GetHeight (Graphics
.systemDpiY
);
578 public static Font
FromLogFont (object lf
)
580 if (GDIPlus
.RunningOnUnix ())
581 return FromLogFont(lf
, IntPtr
.Zero
);
583 // win32 specific code
584 IntPtr hDC
= IntPtr
.Zero
;
586 hDC
= GDIPlus
.GetDC(IntPtr
.Zero
);
587 return FromLogFont (lf
, hDC
);
590 GDIPlus
.ReleaseDC (IntPtr
.Zero
, hDC
);
594 [SecurityPermission (SecurityAction
.Demand
, UnmanagedCode
= true)]
595 public void ToLogFont (object logFont
)
597 if (GDIPlus
.RunningOnUnix ()) {
598 // Unix - We don't have a window we could associate the DC with
599 // so we use an image instead
600 using (Bitmap img
= new Bitmap (1, 1, Imaging
.PixelFormat
.Format32bppArgb
)) {
601 using (Graphics g
= Graphics
.FromImage (img
)) {
602 ToLogFont (logFont
, g
);
607 IntPtr hDC
= GDIPlus
.GetDC (IntPtr
.Zero
);
609 using (Graphics g
= Graphics
.FromHdc (hDC
)) {
610 ToLogFont (logFont
, g
);
614 GDIPlus
.ReleaseDC (IntPtr
.Zero
, hDC
);
619 [SecurityPermission (SecurityAction
.Demand
, UnmanagedCode
= true)]
620 public void ToLogFont (object logFont
, Graphics graphics
)
622 if (graphics
== null)
623 throw new ArgumentNullException ("graphics");
625 if (logFont
== null) {
627 throw new AccessViolationException ("logFont");
629 throw new NullReferenceException ("logFont");
633 Type st
= logFont
.GetType ();
634 if (!st
.IsLayoutSequential
)
635 throw new ArgumentException ("logFont", Locale
.GetText ("Layout must be sequential."));
637 // note: there is no exception if 'logFont' isn't big enough
638 Type lf
= typeof (LOGFONT
);
639 int size
= Marshal
.SizeOf (logFont
);
640 if (size
>= Marshal
.SizeOf (lf
)) {
642 IntPtr copy
= Marshal
.AllocHGlobal (size
);
644 Marshal
.StructureToPtr (logFont
, copy
, false);
646 status
= GDIPlus
.GdipGetLogFont (NativeObject
, graphics
.NativeObject
, logFont
);
647 if (status
!= Status
.Ok
) {
648 // reset to original values
649 Marshal
.PtrToStructure (copy
, logFont
);
653 Marshal
.FreeHGlobal (copy
);
656 if (CharSetOffset
== -1) {
657 // not sure why this methods returns an IntPtr since it's an offset
658 // anyway there's no issue in downcasting the result into an int32
659 CharSetOffset
= (int) Marshal
.OffsetOf (lf
, "lfCharSet");
662 // note: Marshal.WriteByte(object,*) methods are unimplemented on Mono
663 GCHandle gch
= GCHandle
.Alloc (logFont
, GCHandleType
.Pinned
);
665 IntPtr ptr
= gch
.AddrOfPinnedObject ();
666 // if GDI+ lfCharSet is 0, then we return (S.D.) 1, otherwise the value is unchanged
667 if (Marshal
.ReadByte (ptr
, CharSetOffset
) == 0) {
668 // set lfCharSet to 1
669 Marshal
.WriteByte (ptr
, CharSetOffset
, 1);
676 // now we can throw, if required
677 GDIPlus
.CheckStatus (status
);
681 public float GetHeight (Graphics graphics
)
683 if (graphics
== null)
684 throw new ArgumentNullException ("graphics");
687 Status status
= GDIPlus
.GdipGetFontHeight (fontObject
, graphics
.NativeObject
, out size
);
688 GDIPlus
.CheckStatus (status
);
692 public float GetHeight (float dpi
)
695 Status status
= GDIPlus
.GdipGetFontHeightGivenDPI (fontObject
, dpi
, out size
);
696 GDIPlus
.CheckStatus (status
);
700 public override String
ToString ()
702 return String
.Format ("[Font: Name={0}, Size={1}, Units={2}, GdiCharSet={3}, GdiVerticalFont={4}]", _name
, Size
, (int)_unit
, _gdiCharSet
, _gdiVerticalFont
);