fixed tests
[mono.git] / mcs / class / System.Drawing / System.Drawing / Font.cs
1 //
2 // System.Drawing.Fonts.cs
3 //
4 // Authors:
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)
10 //
11 // Copyright (C) 2004 Ximian, Inc. (http://www.ximian.com)
12 // Copyright (C) 2004, 2006 Novell, Inc (http://www.novell.com)
13 //
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:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
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.
32 //
33
34 using System.Runtime.Serialization;
35 using System.Runtime.InteropServices;
36 using System.Security.Permissions;
37 using System.ComponentModel;
38
39 namespace System.Drawing
40 {
41         [Serializable]
42         [ComVisible (true)]
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
46         {
47                 private IntPtr  fontObject = IntPtr.Zero;
48                 private string  systemFontName;
49                 private float _size;
50                 private object olf;
51
52                 private const byte DefaultCharSet = 1;
53                 private static int CharSetOffset = -1;
54
55                 private void CreateFont (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
56                 {
57 #if ONLY_1_1
58                         if (familyName == null)
59                                 throw new ArgumentNullException ("familyName");
60 #endif
61                         FontFamily family;
62                         // NOTE: If family name is null, empty or invalid,
63                         // MS creates Microsoft Sans Serif font.
64                         try {
65                                 family = new FontFamily (familyName);
66                         }
67                         catch (Exception){
68                                 family = FontFamily.GenericSansSerif;
69                         }
70
71                         setProperties (family, emSize, style, unit, charSet, isVertical);           
72                         Status status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style, unit, out fontObject);
73                         GDIPlus.CheckStatus (status);
74                 }
75
76                 private Font (SerializationInfo info, StreamingContext context)
77                 {
78                         string          name;
79                         float           size;
80                         FontStyle       style;
81                         GraphicsUnit    unit;
82
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));
87  
88                         CreateFont(name, size, style, unit, DefaultCharSet, false);
89                 }
90
91                 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
92                 {
93                         info.AddValue("Name", Name);
94                         info.AddValue("Size", Size);
95                         info.AddValue("Style", Style);
96                         info.AddValue("Unit", Unit);
97                 }
98
99                 ~Font()
100                 {
101                         Dispose ();
102                 }
103
104                 public void Dispose ()
105                 {
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);
112                         }
113                 }
114
115                 internal void unitConversion (GraphicsUnit fromUnit, GraphicsUnit toUnit, float nSrc, out float nTrg)
116                 {
117                         float inchs = 0;
118                         nTrg = 0;
119                         
120                         switch (fromUnit) {
121                         case GraphicsUnit.Display:
122                                 inchs = nSrc / 75f;
123                                 break;
124                         case GraphicsUnit.Document:
125                                 inchs = nSrc / 300f;
126                                 break;
127                         case GraphicsUnit.Inch:
128                                 inchs = nSrc;
129                                 break;
130                         case GraphicsUnit.Millimeter:
131                                 inchs = nSrc / 25.4f;
132                                 break;
133                         case GraphicsUnit.Pixel:
134                         case GraphicsUnit.World:
135                                 inchs = nSrc / Graphics.systemDpiX;
136                                 break;
137                         case GraphicsUnit.Point:
138                                 inchs = nSrc / 72f;
139                                 break;
140                         default:
141                                 throw new ArgumentException("Invalid GraphicsUnit");
142                         }
143
144                         switch (toUnit) {
145                         case GraphicsUnit.Display:
146                                 nTrg = inchs * 75;
147                                 break;
148                         case GraphicsUnit.Document:
149                                 nTrg = inchs * 300;
150                                 break;
151                         case GraphicsUnit.Inch:
152                                 nTrg = inchs;
153                                 break;
154                         case GraphicsUnit.Millimeter:
155                                 nTrg = inchs * 25.4f;
156                                 break;
157                         case GraphicsUnit.Pixel:
158                         case GraphicsUnit.World:
159                                 nTrg = inchs * Graphics.systemDpiX;
160                                 break;
161                         case GraphicsUnit.Point:
162                                 nTrg = inchs * 72;
163                                 break;
164                         default:
165                                 throw new ArgumentException("Invalid GraphicsUnit");
166                         }
167                 }
168
169                 internal void setProperties (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
170                 {
171                         _name = family.Name;
172                         _fontFamily = family;                   
173                         _size = emSize;
174
175                         // MS throws ArgumentException, if unit is set to GraphicsUnit.Display
176                         _unit = unit;
177                         _style = style;
178                         _gdiCharSet = charSet;
179                         _gdiVerticalFont = isVertical;
180                         
181                         unitConversion (unit, GraphicsUnit.Point, emSize, out  _sizeInPoints);
182                                                 
183                         _bold = _italic = _strikeout = _underline = false;
184
185                         if ((style & FontStyle.Bold) == FontStyle.Bold)
186                                 _bold = true;
187                                 
188                         if ((style & FontStyle.Italic) == FontStyle.Italic)
189                                _italic = true;
190
191                         if ((style & FontStyle.Strikeout) == FontStyle.Strikeout)
192                                 _strikeout = true;
193
194                         if ((style & FontStyle.Underline) == FontStyle.Underline)
195                                 _underline = true;                  
196                 }
197
198                 public static Font FromHfont (IntPtr Hfont)
199                 {
200                         IntPtr                  newObject;
201                         IntPtr                  hdc;                    
202                         FontStyle               newStyle = FontStyle.Regular;
203                         float                   newSize;
204                         LOGFONT                 lf = new LOGFONT ();
205
206                         // Sanity. Should we throw an exception?
207                         if (Hfont == IntPtr.Zero) {
208                                 Font result = new Font ("Arial", (float)10.0, FontStyle.Regular);
209                                 return(result);
210                         }
211
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);
217                         } else {
218
219                                 // This needs testing
220                                 // GetDC, SelectObject, ReleaseDC GetTextMetric and
221                                 // GetFontFace are not really GDIPlus, see gdipFunctions.cs
222
223                                 newStyle = FontStyle.Regular;
224
225                                 hdc = GDIPlus.GetDC (IntPtr.Zero);
226                                 try {
227                                         return FromLogFont (lf, hdc);
228                                 }
229                                 finally {
230                                         GDIPlus.ReleaseDC (IntPtr.Zero, hdc);
231                                 }
232                         }
233                         
234                         if (lf.lfItalic != 0) {
235                                 newStyle |= FontStyle.Italic;
236                         }
237
238                         if (lf.lfUnderline != 0) {
239                                 newStyle |= FontStyle.Underline;
240                         }
241
242                         if (lf.lfStrikeOut != 0) {
243                                 newStyle |= FontStyle.Strikeout;
244                         }
245
246                         if (lf.lfWeight > 400) {
247                                 newStyle |= FontStyle.Bold;
248                         }
249
250                         if (lf.lfHeight < 0) {
251                                 newSize = lf.lfHeight * -1;
252                         } else {
253                                 newSize = lf.lfHeight;
254                         }
255
256                         return (new Font (newObject, lf.lfFaceName, newStyle, newSize));
257                 }
258
259                 public IntPtr ToHfont ()
260                 {
261                         if (fontObject == IntPtr.Zero)
262                                 throw new ArgumentException (Locale.GetText ("Object has been disposed."));
263
264                         if (GDIPlus.RunningOnUnix ())
265                                 return fontObject;
266
267                         // win32 specific code
268                         if (olf == null) {
269                                 olf = new LOGFONT ();
270                                 ToLogFont(olf);
271                         }
272                         LOGFONT lf = (LOGFONT)olf;
273                         return GDIPlus.CreateFontIndirect (ref lf);
274                 }
275
276                 internal Font (IntPtr newFontObject, string familyName, FontStyle style, float size)
277                 {
278                         FontFamily fontFamily;                  
279                         
280                         try {
281                                 fontFamily = new FontFamily (familyName);
282                         }
283                         catch (Exception){
284                                 fontFamily = FontFamily.GenericSansSerif;
285                         }
286                         
287                         setProperties (fontFamily, size, style, GraphicsUnit.Pixel, 0, false);
288                         fontObject = newFontObject;
289                 }
290
291                 public Font (Font original, FontStyle style)
292                 {
293                         Status status;
294                         setProperties (original.FontFamily, original.Size, style, original.Unit, original.GdiCharSet, original.GdiVerticalFont);
295                                 
296                         status = GDIPlus.GdipCreateFont (_fontFamily.NativeObject,      Size,  Style,   Unit,  out fontObject);
297                         GDIPlus.CheckStatus (status);                   
298                 }
299
300                 public Font (FontFamily family, float emSize,  GraphicsUnit unit)
301                         : this (family, emSize, FontStyle.Regular, unit, DefaultCharSet, false)
302                 {
303                 }
304
305                 public Font (string familyName, float emSize,  GraphicsUnit unit)
306                         : this (new FontFamily (familyName), emSize, FontStyle.Regular, unit, DefaultCharSet, false)
307                 {
308                 }
309
310                 public Font (FontFamily family, float emSize)
311                         : this (family, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
312                 {
313                 }
314
315                 public Font (FontFamily family, float emSize, FontStyle style)
316                         : this (family, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
317                 {
318                 }
319
320                 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
321                         : this (family, emSize, style, unit, DefaultCharSet, false)
322                 {
323                 }
324
325                 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
326                         : this (family, emSize, style, unit, charSet, false)
327                 {
328                 }
329
330                 public Font (FontFamily family, float emSize, FontStyle style,
331                                 GraphicsUnit unit, byte charSet, bool isVertical)
332                 {
333                         if (family == null)
334                                 throw new ArgumentNullException ("family");
335
336                         Status status;
337                         setProperties (family, emSize, style, unit, charSet, isVertical);               
338                         status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style,   unit,  out fontObject);
339                         GDIPlus.CheckStatus (status);
340                 }
341
342                 public Font (string familyName, float emSize)
343                         : this (familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, DefaultCharSet, false)
344                 {
345                 }
346
347                 public Font (string familyName, float emSize, FontStyle style)
348                         : this (familyName, emSize, style, GraphicsUnit.Point, DefaultCharSet, false)
349                 {
350                 }
351
352                 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit)
353                         : this (familyName, emSize, style, unit, DefaultCharSet, false)
354                 {
355                 }
356
357                 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
358                         : this (familyName, emSize, style, unit, charSet, false)
359                 {
360                 }
361
362                 public Font (string familyName, float emSize, FontStyle style,
363                                 GraphicsUnit unit, byte charSet, bool isVertical)
364                 {
365                         CreateFont(familyName, emSize, style, unit, charSet, isVertical);
366                 }
367
368                 public object Clone ()
369                 {
370                         return new Font (this, Style);
371                 }
372
373                 internal IntPtr NativeObject {            
374                         get {
375                                 return fontObject;
376                         }
377                 }
378
379 #if NET_2_0
380                 internal string SysFontName {
381                         set {
382                                 systemFontName = value;
383                         }
384                 }
385 #endif
386
387                 private bool _bold;
388
389                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
390                 public bool Bold {
391                         get {
392                                 return _bold;
393                         }
394                 }
395
396                 private FontFamily _fontFamily;
397
398                 [Browsable (false)]
399                 public FontFamily FontFamily {
400                         get {
401                                 return _fontFamily;
402                         }
403                 }
404
405                 private byte _gdiCharSet;
406
407                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
408                 public byte GdiCharSet {
409                         get {
410                                 return _gdiCharSet;
411                         }
412                 }
413
414                 private bool _gdiVerticalFont;
415
416                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
417                 public bool GdiVerticalFont {
418                         get {
419                                 return _gdiVerticalFont;
420                         }
421                 }
422
423                 [Browsable (false)]
424                 public int Height {
425                         get {
426                                 return (int) Math.Ceiling (GetHeight ());
427                         }
428                 }
429
430 #if NET_2_0
431                 [Browsable(false)]
432                 public bool IsSystemFont {
433                         get {
434                                 if (systemFontName == null)
435                                         return false;
436
437                                 return StringComparer.InvariantCulture.Compare (systemFontName, string.Empty) != 0;
438                         }
439                 }
440 #endif
441
442                 private bool _italic;
443
444                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
445                 public bool Italic {
446                         get {
447                                 return _italic;
448                         }
449                 }
450
451                 private string _name;
452
453                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
454                 [Editor ("System.Drawing.Design.FontNameEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
455                 [TypeConverter (typeof (FontConverter.FontNameConverter))]
456                 public string Name {
457                         get {
458                                 return _name;
459                         }
460                 }
461                 
462                 public float Size {
463                         get {
464                                 return _size;                   
465                         }
466                 }
467
468                 private float _sizeInPoints;
469
470                 [Browsable (false)]
471                 public float SizeInPoints {
472                         get {
473                                 return _sizeInPoints;
474                         }
475                 }
476
477                 private bool _strikeout;
478
479                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
480                 public bool Strikeout {
481                         get {
482                                 return _strikeout;
483                         }
484                 }
485                 
486                 private FontStyle _style;
487
488                 [Browsable (false)]
489                 public FontStyle Style {
490                         get {
491                                 return _style;
492                         }
493                 }
494
495 #if NET_2_0
496                 [Browsable(false)]
497                 public string SystemFontName {
498                         get {
499                                 return systemFontName;
500                         }
501                 }
502 #endif
503                 private bool _underline;
504
505                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
506                 public bool Underline {
507                         get {
508                                 return _underline;
509                         }
510                 }
511
512                 private GraphicsUnit _unit;
513
514                 [TypeConverter (typeof (FontConverter.FontUnitConverter))]
515                 public GraphicsUnit Unit {
516                         get {
517                                 return _unit;
518                         }
519                 }
520
521                 public override bool Equals (object obj)
522                 {
523                         Font fnt = (obj as Font);
524                         if (fnt == null)
525                                 return false;
526
527                         if (fnt.FontFamily.Equals (FontFamily) && fnt.Size == Size &&
528                             fnt.Style == Style && fnt.Unit == Unit &&
529                             fnt.GdiCharSet == GdiCharSet && 
530                             fnt.GdiVerticalFont == GdiVerticalFont)
531                                 return true;
532                         else
533                                 return false;
534                 }
535
536                 public override int GetHashCode ()
537                 {
538                         return _name.GetHashCode ();
539                 }
540
541                 [MonoTODO ("The hdc parameter has no direct equivalent in libgdiplus.")]
542                 public static Font FromHdc (IntPtr hdc)
543                 {
544                         throw new NotImplementedException ();
545                 }
546
547                 [MonoTODO ("The returned font may not have all it's properties initialized correctly.")]
548                 public static Font FromLogFont (object lf, IntPtr hdc)
549                 {
550                         IntPtr newObject;
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);
555                 }
556
557                 public float GetHeight ()
558                 {
559                         return GetHeight (Graphics.systemDpiY);
560                 }
561
562                 public static Font FromLogFont (object lf)
563                 {
564                         if (GDIPlus.RunningOnUnix ())
565                                 return FromLogFont(lf, IntPtr.Zero);
566
567                         // win32 specific code
568                         IntPtr hDC = IntPtr.Zero;
569                         try {
570                                 hDC = GDIPlus.GetDC(IntPtr.Zero);
571                                 return FromLogFont (lf, hDC);
572                         }
573                         finally {
574                                 GDIPlus.ReleaseDC (IntPtr.Zero, hDC);
575                         }
576                 }
577
578                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
579                 public void ToLogFont (object logFont)
580                 {
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);
587                                         }
588                                 }
589                         } else {
590                                 // Windows
591                                 IntPtr hDC = GDIPlus.GetDC (IntPtr.Zero);
592                                 try {
593                                         using (Graphics g = Graphics.FromHdc (hDC)) {
594                                                 ToLogFont (logFont, g);
595                                         }
596                                 }
597                                 finally {
598                                         GDIPlus.ReleaseDC (IntPtr.Zero, hDC);
599                                 }
600                         }
601                 }
602
603                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
604                 public void ToLogFont (object logFont, Graphics graphics)
605                 {
606                         if (graphics == null)
607                                 throw new ArgumentNullException ("graphics");
608
609                         if (logFont == null) {
610 #if NET_2_0
611                                 throw new AccessViolationException ("logFont");
612 #else
613                                 throw new NullReferenceException ("logFont");
614 #endif
615                         }
616
617                         Type st = logFont.GetType ();
618                         if (!st.IsLayoutSequential)
619                                 throw new ArgumentException ("logFont", Locale.GetText ("Layout must be sequential."));
620
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)) {
625                                 Status status;
626                                 IntPtr copy = Marshal.AllocHGlobal (size);
627                                 try {
628                                         Marshal.StructureToPtr (logFont, copy, false);
629
630                                         status = GDIPlus.GdipGetLogFont (NativeObject, graphics.NativeObject, logFont);
631                                         if (status != Status.Ok) {
632                                                 // reset to original values
633                                                 Marshal.PtrToStructure (copy, logFont);
634                                         }
635                                 }
636                                 finally {
637                                         Marshal.FreeHGlobal (copy);
638                                 }
639
640                                 if (CharSetOffset == -1) {
641                                         CharSetOffset = (int) Marshal.OffsetOf (lf, "lfCharSet");
642                                 }
643
644                                 // note: Marshal.WriteByte(object,*) methods are unimplemented on Mono
645                                 GCHandle gch = GCHandle.Alloc (logFont, GCHandleType.Pinned);
646                                 try {
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);
652                                         }
653                                 }
654                                 finally {
655                                         gch.Free ();
656                                 }
657
658                                 // now we can throw, if required
659                                 GDIPlus.CheckStatus (status);
660                         }
661                 }
662
663                 public float GetHeight (Graphics graphics)
664                 {
665                         float size;
666                         Status status = GDIPlus.GdipGetFontHeight (fontObject, graphics.NativeObject, out size);
667                         GDIPlus.CheckStatus (status);
668                         return size;
669                 }
670
671                 public float GetHeight (float dpi)
672                 {
673                         float size;
674                         Status status = GDIPlus.GdipGetFontHeightGivenDPI (fontObject, dpi, out size);
675                         GDIPlus.CheckStatus (status);
676                         return size;
677                 }
678
679                 public override String ToString ()
680                 {
681                         return String.Format ("[Font: Name={0}, Size={1}, Units={2}, GdiCharSet={3}, GdiVerticalFont={4}]", _name, Size, (int)_unit, _gdiCharSet, _gdiVerticalFont);
682                 }
683         }
684 }