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;
48 private string systemFontName;
52 private const byte DefaultCharSet = 1;
53 private static int CharSetOffset = -1;
55 private void CreateFont (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
58 if (familyName == null)
59 throw new ArgumentNullException ("familyName");
62 // NOTE: If family name is null, empty or invalid,
63 // MS creates Microsoft Sans Serif font.
65 family = new FontFamily (familyName);
68 family = FontFamily.GenericSansSerif;
71 setProperties (family, emSize, style, unit, charSet, isVertical);
72 Status status = GDIPlus.GdipCreateFont (family.NativeObject, emSize, style, unit, out fontObject);
73 GDIPlus.CheckStatus (status);
76 private Font (SerializationInfo info, StreamingContext context)
83 name = (string)info.GetValue("Name", typeof(string));
84 size = (float)info.GetValue("Size", typeof(float));
85 style = (FontStyle)info.GetValue("Style", typeof(FontStyle));
86 unit = (GraphicsUnit)info.GetValue("Unit", typeof(GraphicsUnit));
88 CreateFont(name, size, style, unit, DefaultCharSet, false);
91 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
93 info.AddValue("Name", Name);
94 info.AddValue("Size", Size);
95 info.AddValue("Style", Style);
96 info.AddValue("Unit", Unit);
104 public void Dispose ()
106 if (fontObject != IntPtr.Zero) {
107 Status status = GDIPlus.GdipDeleteFont (fontObject);
108 fontObject = IntPtr.Zero;
109 GC.SuppressFinalize (this);
110 // check the status code (throw) at the last step
111 GDIPlus.CheckStatus (status);
115 internal void unitConversion (GraphicsUnit fromUnit, GraphicsUnit toUnit, float nSrc, out float nTrg)
121 case GraphicsUnit.Display:
124 case GraphicsUnit.Document:
127 case GraphicsUnit.Inch:
130 case GraphicsUnit.Millimeter:
131 inchs = nSrc / 25.4f;
133 case GraphicsUnit.Pixel:
134 case GraphicsUnit.World:
135 inchs = nSrc / Graphics.systemDpiX;
137 case GraphicsUnit.Point:
141 throw new ArgumentException("Invalid GraphicsUnit");
145 case GraphicsUnit.Display:
148 case GraphicsUnit.Document:
151 case GraphicsUnit.Inch:
154 case GraphicsUnit.Millimeter:
155 nTrg = inchs * 25.4f;
157 case GraphicsUnit.Pixel:
158 case GraphicsUnit.World:
159 nTrg = inchs * Graphics.systemDpiX;
161 case GraphicsUnit.Point:
165 throw new ArgumentException("Invalid GraphicsUnit");
169 internal void setProperties (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
172 _fontFamily = family;
175 // MS throws ArgumentException, if unit is set to GraphicsUnit.Display
178 _gdiCharSet = charSet;
179 _gdiVerticalFont = isVertical;
181 unitConversion (unit, GraphicsUnit.Point, emSize, out _sizeInPoints);
183 _bold = _italic = _strikeout = _underline = false;
185 if ((style & FontStyle.Bold) == FontStyle.Bold)
188 if ((style & FontStyle.Italic) == FontStyle.Italic)
191 if ((style & FontStyle.Strikeout) == FontStyle.Strikeout)
194 if ((style & FontStyle.Underline) == FontStyle.Underline)
198 public static Font FromHfont (IntPtr Hfont)
202 FontStyle newStyle = FontStyle.Regular;
204 LOGFONT lf = new LOGFONT ();
206 // Sanity. Should we throw an exception?
207 if (Hfont == IntPtr.Zero) {
208 Font result = new Font ("Arial", (float)10.0, FontStyle.Regular);
212 if (GDIPlus.RunningOnUnix ()) {
213 // If we're on Unix we use our private gdiplus API to avoid Wine
214 // dependencies in S.D
215 Status s = GDIPlus.GdipCreateFontFromHfont (Hfont, out newObject, ref lf);
216 GDIPlus.CheckStatus (s);
219 // This needs testing
220 // GetDC, SelectObject, ReleaseDC GetTextMetric and
221 // GetFontFace are not really GDIPlus, see gdipFunctions.cs
223 newStyle = FontStyle.Regular;
225 hdc = GDIPlus.GetDC (IntPtr.Zero);
227 return FromLogFont (lf, hdc);
230 GDIPlus.ReleaseDC (IntPtr.Zero, hdc);
234 if (lf.lfItalic != 0) {
235 newStyle |= FontStyle.Italic;
238 if (lf.lfUnderline != 0) {
239 newStyle |= FontStyle.Underline;
242 if (lf.lfStrikeOut != 0) {
243 newStyle |= FontStyle.Strikeout;
246 if (lf.lfWeight > 400) {
247 newStyle |= FontStyle.Bold;
250 if (lf.lfHeight < 0) {
251 newSize = lf.lfHeight * -1;
253 newSize = lf.lfHeight;
256 return (new Font (newObject, lf.lfFaceName, newStyle, newSize));
259 public IntPtr ToHfont ()
261 if (fontObject == IntPtr.Zero)
262 throw new ArgumentException (Locale.GetText ("Object has been disposed."));
264 if (GDIPlus.RunningOnUnix ())
267 // win32 specific code
269 olf = new LOGFONT ();
272 LOGFONT lf = (LOGFONT)olf;
273 return GDIPlus.CreateFontIndirect (ref lf);
276 internal Font (IntPtr newFontObject, string familyName, FontStyle style, float size)
278 FontFamily fontFamily;
281 fontFamily = new FontFamily (familyName);
284 fontFamily = FontFamily.GenericSansSerif;
287 setProperties (fontFamily, size, style, GraphicsUnit.Pixel, 0, false);
288 fontObject = newFontObject;
291 public Font (Font original, FontStyle style)
294 setProperties (original.FontFamily, original.Size, style, original.Unit, original.GdiCharSet, original.GdiVerticalFont);
296 status = GDIPlus.GdipCreateFont (_fontFamily.NativeObject, Size, Style, Unit, out fontObject);
297 GDIPlus.CheckStatus (status);
300 public Font (FontFamily family, float emSize, GraphicsUnit unit)
301 : this (family, emSize, FontStyle.Regular, unit, DefaultCharSet, false)
305 public Font (string familyName, float emSize, GraphicsUnit unit)
306 : this (new FontFamily (familyName), emSize, FontStyle.Regular, unit, DefaultCharSet, false)
310 public Font (FontFamily family, float emSize)
311 : this (family, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
315 public Font (FontFamily family, float emSize, FontStyle style)
316 : this (family, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
320 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
321 : this (family, emSize, style, unit, DefaultCharSet, false)
325 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
326 : this (family, emSize, style, unit, charSet, false)
330 public Font (FontFamily family, float emSize, FontStyle style,
331 GraphicsUnit unit, byte charSet, bool isVertical)
334 throw new ArgumentNullException ("family");
337 setProperties (family, emSize, style, unit, charSet, isVertical);
338 status = GDIPlus.GdipCreateFont (family.NativeObject, emSize, style, unit, out fontObject);
339 GDIPlus.CheckStatus (status);
342 public Font (string familyName, float emSize)
343 : this (familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
347 public Font (string familyName, float emSize, FontStyle style)
348 : this (familyName, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
352 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit)
353 : this (familyName, emSize, style, unit, DefaultCharSet, false)
357 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
358 : this (familyName, emSize, style, unit, charSet, false)
362 public Font (string familyName, float emSize, FontStyle style,
363 GraphicsUnit unit, byte charSet, bool isVertical)
365 CreateFont(familyName, emSize, style, unit, charSet, isVertical);
368 public object Clone ()
370 return new Font (this, Style);
373 internal IntPtr NativeObject {
380 internal string SysFontName {
382 systemFontName = value;
389 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
396 private FontFamily _fontFamily;
399 public FontFamily FontFamily {
405 private byte _gdiCharSet;
407 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
408 public byte GdiCharSet {
414 private bool _gdiVerticalFont;
416 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
417 public bool GdiVerticalFont {
419 return _gdiVerticalFont;
426 return (int) Math.Ceiling (GetHeight ());
432 public bool IsSystemFont {
434 if (systemFontName == null)
437 return StringComparer.InvariantCulture.Compare (systemFontName, string.Empty) != 0;
442 private bool _italic;
444 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
451 private string _name;
453 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
454 [Editor ("System.Drawing.Design.FontNameEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
455 [TypeConverter (typeof (FontConverter.FontNameConverter))]
468 private float _sizeInPoints;
471 public float SizeInPoints {
473 return _sizeInPoints;
477 private bool _strikeout;
479 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
480 public bool Strikeout {
486 private FontStyle _style;
489 public FontStyle Style {
497 public string SystemFontName {
499 return systemFontName;
503 private bool _underline;
505 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
506 public bool Underline {
512 private GraphicsUnit _unit;
514 [TypeConverter (typeof (FontConverter.FontUnitConverter))]
515 public GraphicsUnit Unit {
521 public override bool Equals (object obj)
523 Font fnt = (obj as Font);
527 if (fnt.FontFamily.Equals (FontFamily) && fnt.Size == Size &&
528 fnt.Style == Style && fnt.Unit == Unit &&
529 fnt.GdiCharSet == GdiCharSet &&
530 fnt.GdiVerticalFont == GdiVerticalFont)
536 public override int GetHashCode ()
538 return _name.GetHashCode ();
541 [MonoTODO ("The hdc parameter has no direct equivalent in libgdiplus.")]
542 public static Font FromHdc (IntPtr hdc)
544 throw new NotImplementedException ();
547 [MonoTODO ("The returned font may not have all it's properties initialized correctly.")]
548 public static Font FromLogFont (object lf, IntPtr hdc)
551 LOGFONT o = (LOGFONT)lf;
552 Status status = GDIPlus.GdipCreateFontFromLogfont (hdc, ref o, out newObject);
553 GDIPlus.CheckStatus (status);
554 return new Font (newObject, "Microsoft Sans Serif", FontStyle.Regular, 10);
557 public float GetHeight ()
559 return GetHeight (Graphics.systemDpiY);
562 public static Font FromLogFont (object lf)
564 if (GDIPlus.RunningOnUnix ())
565 return FromLogFont(lf, IntPtr.Zero);
567 // win32 specific code
568 IntPtr hDC = IntPtr.Zero;
570 hDC = GDIPlus.GetDC(IntPtr.Zero);
571 return FromLogFont (lf, hDC);
574 GDIPlus.ReleaseDC (IntPtr.Zero, hDC);
578 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
579 public void ToLogFont (object logFont)
581 if (GDIPlus.RunningOnUnix ()) {
582 // Unix - We don't have a window we could associate the DC with
583 // so we use an image instead
584 using (Bitmap img = new Bitmap (1, 1, Imaging.PixelFormat.Format32bppArgb)) {
585 using (Graphics g = Graphics.FromImage (img)) {
586 ToLogFont (logFont, g);
591 IntPtr hDC = GDIPlus.GetDC (IntPtr.Zero);
593 using (Graphics g = Graphics.FromHdc (hDC)) {
594 ToLogFont (logFont, g);
598 GDIPlus.ReleaseDC (IntPtr.Zero, hDC);
603 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
604 public void ToLogFont (object logFont, Graphics graphics)
606 if (graphics == null)
607 throw new ArgumentNullException ("graphics");
609 if (logFont == null) {
611 throw new AccessViolationException ("logFont");
613 throw new NullReferenceException ("logFont");
617 Type st = logFont.GetType ();
618 if (!st.IsLayoutSequential)
619 throw new ArgumentException ("logFont", Locale.GetText ("Layout must be sequential."));
621 // note: there is no exception if 'logFont' isn't big enough
622 Type lf = typeof (LOGFONT);
623 int size = Marshal.SizeOf (logFont);
624 if (size >= Marshal.SizeOf (lf)) {
626 IntPtr copy = Marshal.AllocHGlobal (size);
628 Marshal.StructureToPtr (logFont, copy, false);
630 status = GDIPlus.GdipGetLogFont (NativeObject, graphics.NativeObject, logFont);
631 if (status != Status.Ok) {
632 // reset to original values
633 Marshal.PtrToStructure (copy, logFont);
637 Marshal.FreeHGlobal (copy);
640 if (CharSetOffset == -1) {
641 CharSetOffset = (int) Marshal.OffsetOf (lf, "lfCharSet");
644 // note: Marshal.WriteByte(object,*) methods are unimplemented on Mono
645 GCHandle gch = GCHandle.Alloc (logFont, GCHandleType.Pinned);
647 IntPtr ptr = gch.AddrOfPinnedObject ();
648 // if GDI+ lfCharSet is 0, then we return (S.D.) 1, otherwise the value is unchanged
649 if (Marshal.ReadByte (ptr, CharSetOffset) == 0) {
650 // set lfCharSet to 1
651 Marshal.WriteByte (ptr, CharSetOffset, 1);
658 // now we can throw, if required
659 GDIPlus.CheckStatus (status);
663 public float GetHeight (Graphics graphics)
666 Status status = GDIPlus.GdipGetFontHeight (fontObject, graphics.NativeObject, out size);
667 GDIPlus.CheckStatus (status);
671 public float GetHeight (float dpi)
674 Status status = GDIPlus.GdipGetFontHeightGivenDPI (fontObject, dpi, out size);
675 GDIPlus.CheckStatus (status);
679 public override String ToString ()
681 return String.Format ("[Font: Name={0}, Size={1}, Units={2}, GdiCharSet={3}, GdiVerticalFont={4}]", _name, Size, (int)_unit, _gdiCharSet, _gdiVerticalFont);