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);