backport this one too
[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 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Runtime.Serialization;
37 using System.Runtime.InteropServices;
38 using System.ComponentModel;
39
40 namespace System.Drawing
41 {
42         [Serializable]
43         [ComVisible (true)]
44         [Editor ("System.Drawing.Design.FontEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
45         [TypeConverter (typeof (FontConverter))]
46         public sealed class Font : MarshalByRefObject, ISerializable, ICloneable, IDisposable
47         {
48                 private IntPtr  fontObject = IntPtr.Zero;
49
50                 private Font (SerializationInfo info, StreamingContext context)
51                 {
52                 }
53
54                 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
55                 {
56                 }
57
58                 ~Font()
59                 {
60                         Dispose ();
61                 }
62
63                 public void Dispose ()
64                 {
65                         if (fontObject != IntPtr.Zero) {
66                                 GDIPlus.CheckStatus (GDIPlus.GdipDeleteFont (fontObject));
67                                 fontObject = IntPtr.Zero;
68                                 GC.SuppressFinalize (this);
69                         }
70                 }
71
72                 internal void unitConversion (GraphicsUnit fromUnit, GraphicsUnit toUnit, float nSrc, out float nTrg)
73                 {
74                         float inchs = 0;
75                         nTrg = 0;
76                         
77                         switch (fromUnit) {
78                         case GraphicsUnit.Display:
79                                 inchs = nSrc / 75f;
80                                 break;
81                         case GraphicsUnit.Document:
82                                 inchs = nSrc / 300f;
83                                 break;
84                         case GraphicsUnit.Inch:
85                                 inchs = nSrc;
86                                 break;
87                         case GraphicsUnit.Millimeter:
88                                 inchs = nSrc / 25.4f;
89                                 break;
90                         case GraphicsUnit.Pixel:
91                         case GraphicsUnit.World:
92                                 inchs = nSrc / Graphics.systemDpiX;
93                                 break;
94                         case GraphicsUnit.Point:
95                                 inchs = nSrc / 72f;
96                                 break;
97                         default:
98                                 throw new ArgumentException("Invalid GraphicsUnit");
99                         }
100
101                         switch (toUnit) {
102                         case GraphicsUnit.Display:
103                                 nTrg = inchs * 75;
104                                 break;
105                         case GraphicsUnit.Document:
106                                 nTrg = inchs * 300;
107                                 break;
108                         case GraphicsUnit.Inch:
109                                 nTrg = inchs;
110                                 break;
111                         case GraphicsUnit.Millimeter:
112                                 nTrg = inchs * 25.4f;
113                                 break;
114                         case GraphicsUnit.Pixel:
115                         case GraphicsUnit.World:
116                                 nTrg = inchs * Graphics.systemDpiX;
117                                 break;
118                         case GraphicsUnit.Point:
119                                 nTrg = inchs * 72;
120                                 break;
121                         default:
122                                 throw new ArgumentException("Invalid GraphicsUnit");
123                         }
124                 }
125
126                 internal void setProperties (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet, bool isVertical)
127                 {
128                         _name = family.Name;
129                         _fontFamily = family;
130                         _size = emSize;
131
132                         // MS throws ArgumentException, if unit is set to GraphicsUnit.Display
133                         _unit = unit;
134                         _style = style;
135                         _gdiCharSet = charSet;
136                         _gdiVerticalFont = isVertical;
137                         
138                         unitConversion (unit, GraphicsUnit.Point, emSize, out  _sizeInPoints);
139                                                 
140                         _bold = _italic = _strikeout = _underline = false;
141
142                         if ((style & FontStyle.Bold) == FontStyle.Bold)
143                                 _bold = true;
144                                 
145                         if ((style & FontStyle.Italic) == FontStyle.Italic)
146                                _italic = true;
147
148                         if ((style & FontStyle.Strikeout) == FontStyle.Strikeout)
149                                 _strikeout = true;
150
151                         if ((style & FontStyle.Underline) == FontStyle.Underline)
152                                 _underline = true;                  
153                 }
154
155                 public static Font FromHfont (IntPtr Hfont)
156                 {
157                         OperatingSystem osInfo = Environment.OSVersion;
158                         IntPtr                  newObject;
159                         IntPtr                  hdc;                    
160                         FontStyle               newStyle = FontStyle.Regular;
161                         float                   newSize;
162                         LOGFONTA                lf = new LOGFONTA ();
163
164                         // Sanity. Should we throw an exception?
165                         if (Hfont == IntPtr.Zero) {
166                                 Font result = new Font ("Arial", (float)10.0, FontStyle.Regular);
167                                 return(result);
168                         }
169
170                         if ((int) osInfo.Platform == 128 || (int) osInfo.Platform == 4) {
171                         // If we're on Unix we use our private gdiplus API to avoid Wine 
172                         // dependencies in S.D
173
174                                 Status s = GDIPlus.GdipCreateFontFromHfont (Hfont, out newObject, ref lf);
175                                 GDIPlus.CheckStatus (s);
176                                 
177                         } else {
178
179                                 // This needs testing
180                                 // GetDC, SelectObject, ReleaseDC GetTextMetric and
181                                 // GetFontFace are not really GDIPlus, see gdipFunctions.cs
182
183                                 newStyle = FontStyle.Regular;
184
185                                 hdc = GDIPlus.GetDC (IntPtr.Zero);
186                                 Font f = FromLogFont (lf, hdc);
187                                 GDIPlus.ReleaseDC (hdc);
188                                 return f;                               
189                         }
190
191                         if (lf.lfItalic != 0) {
192                                 newStyle |= FontStyle.Italic;
193                         }
194
195                         if (lf.lfUnderline != 0) {
196                                 newStyle |= FontStyle.Underline;
197                         }
198
199                         if (lf.lfStrikeOut != 0) {
200                                 newStyle |= FontStyle.Strikeout;
201                         }
202
203                         if (lf.lfWeight > 400) {
204                                 newStyle |= FontStyle.Bold;
205                         }
206
207                         if (lf.lfHeight < 0) {
208                                 newSize = lf.lfHeight * -1;
209                         } else {
210                                 newSize = lf.lfHeight;
211                         }
212
213                         return (new Font (newObject, lf.lfFaceName, newStyle, newSize));
214                 }
215
216                 public IntPtr ToHfont ()
217                 {
218                         IntPtr Hfont;
219                         OperatingSystem osInfo = Environment.OSVersion;
220
221                         // Sanity. Should we throw an exception?
222                         if (fontObject == IntPtr.Zero) {
223                                 return IntPtr.Zero;
224                         }
225
226                         if ((int) osInfo.Platform == 128 || (int) osInfo.Platform == 4) {
227                                 return fontObject;
228                         } else {
229                                 LOGFONTA lf = new LOGFONTA ();
230                                 ToLogFont(lf);
231                                 Hfont = GDIPlus.CreateFontIndirectA (ref lf);
232                         }
233                         return Hfont;
234                 }
235
236                 internal Font (IntPtr newFontObject, string familyName, FontStyle style, float size)
237                 {
238                         FontFamily fontFamily;                  
239                         
240                         try {
241                                 fontFamily = new FontFamily (familyName);
242                         }
243                         catch (Exception){
244                                 fontFamily = FontFamily.GenericSansSerif;
245                         }
246                         
247                         setProperties (fontFamily, size, style, GraphicsUnit.Pixel, 0, false);
248                         fontObject = newFontObject;
249                 }
250
251                 public Font (Font original, FontStyle style)
252                 {
253                         Status status;
254                         setProperties (original.FontFamily, original.Size, style, original.Unit, original.GdiCharSet, original.GdiVerticalFont);
255                                 
256                         status = GDIPlus.GdipCreateFont (_fontFamily.NativeObject,      Size,  Style,   Unit,  out fontObject);
257                         GDIPlus.CheckStatus (status);                   
258                 }
259
260                 public Font (FontFamily family, float emSize,  GraphicsUnit unit)
261                         : this (family, emSize, FontStyle.Regular, unit, (byte)0, false)
262                 {
263                 }
264
265                 public Font (string familyName, float emSize,  GraphicsUnit unit)
266                         : this (new FontFamily (familyName), emSize, FontStyle.Regular, unit, (byte)0, false)
267                 {
268                 }
269
270                 public Font (FontFamily family, float emSize)
271                         : this (family, emSize, FontStyle.Regular, GraphicsUnit.Point, (byte)0, false)
272                 {
273                 }
274
275                 public Font (FontFamily family, float emSize, FontStyle style)
276                         : this (family, emSize, style, GraphicsUnit.Point, (byte)0, false)
277                 {
278                 }
279
280                 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
281                         : this (family, emSize, style, unit, (byte)0, false)
282                 {
283                 }
284
285                 public Font (FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
286                         : this (family, emSize, style, unit, charSet, false)
287                 {
288                 }
289
290                 public Font (FontFamily family, float emSize, FontStyle style,
291                                 GraphicsUnit unit, byte charSet, bool isVertical)
292                 {
293                         // MS does not accept null family
294                         Status status;
295                         setProperties (family, emSize, style, unit, charSet, isVertical);               
296                         status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style,   unit,  out fontObject);
297                         GDIPlus.CheckStatus (status);
298                 }
299
300                 public Font (string familyName, float emSize)
301                         : this (familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, (byte)0, false)
302                 {
303                 }
304
305                 public Font (string familyName, float emSize, FontStyle style)
306                         : this (familyName, emSize, style, GraphicsUnit.Point, (byte)0, false)
307                 {
308                 }
309
310                 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit)
311                         : this (familyName, emSize, style, unit, (byte)0, false)
312                 {
313                 }
314
315                 public Font (string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte charSet)
316                         : this (familyName, emSize, style, unit, charSet, false)
317                 {
318                 }
319
320                 public Font (string familyName, float emSize, FontStyle style,
321                                 GraphicsUnit unit, byte charSet, bool isVertical)
322                 {
323                         // NOTE: If family name is null, empty or invalid,
324                         // MS creates Microsoft Sans Serif font.
325                         Status status;                  
326                         FontFamily family;                      
327                         try {
328                                 family = new FontFamily (familyName);
329                         }
330                         catch (Exception){
331                                 family = FontFamily.GenericSansSerif;
332                         }
333                         
334                         setProperties (family, emSize, style, unit, charSet, isVertical);                               
335
336                         status = GDIPlus.GdipCreateFont (family.NativeObject, emSize,  style, unit, out fontObject);
337                         GDIPlus.CheckStatus (status);                   
338                 }
339
340                 public object Clone ()
341                 {
342                         return new Font (this, Style);
343                 }
344
345                 internal IntPtr NativeObject {            
346                         get {
347                                         return fontObject;
348                         }
349                         set {
350                                         fontObject = value;
351                         }
352                 }
353
354                 private bool _bold;
355
356                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
357                 public bool Bold {
358                         get {
359                                 return _bold;
360                         }
361                 }
362
363                 private FontFamily _fontFamily;
364
365                 [Browsable (false)]
366                 public FontFamily FontFamily {
367                         get {
368                                 return _fontFamily;
369                         }
370                 }
371
372                 private byte _gdiCharSet;
373
374                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
375                 public byte GdiCharSet {
376                         get {
377                                 return _gdiCharSet;
378                         }
379                 }
380
381                 private bool _gdiVerticalFont;
382
383                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
384                 public bool GdiVerticalFont {
385                         get {
386                                 return _gdiVerticalFont;
387                         }
388                 }
389
390                 [Browsable (false)]
391                 public int Height {
392                         get {
393                                 return (int) Math.Ceiling (GetHeight ());
394                         }
395                 }
396
397                 private bool _italic;
398
399                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
400                 public bool Italic {
401                         get {
402                                 return _italic;
403                         }
404                 }
405
406                 private string _name;
407
408                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
409                 [Editor ("System.Drawing.Design.FontNameEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
410                 [TypeConverter (typeof (FontConverter.FontNameConverter))]
411                 public string Name {
412                         get {
413                                 return _name;
414                         }
415                 }
416
417                 private float _size;
418                 public float Size {
419                         get {
420                                 return _size;
421                         }
422                 }
423
424                 private float _sizeInPoints;
425
426                 [Browsable (false)]
427                 public float SizeInPoints {
428                         get {
429                                 return _sizeInPoints;
430                         }
431                 }
432
433                 private bool _strikeout;
434
435                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
436                 public bool Strikeout {
437                         get {
438                                 return _strikeout;
439                         }
440                 }
441                 
442                 private FontStyle _style;
443
444                 [Browsable (false)]
445                 public FontStyle Style {
446                         get {
447                                 return _style;
448                         }
449                 }
450
451                 private bool _underline;
452
453                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
454                 public bool Underline {
455                         get {
456                                 return _underline;
457                         }
458                 }
459
460                 private GraphicsUnit _unit;
461
462                 [TypeConverter (typeof (FontConverter.FontUnitConverter))]
463                 public GraphicsUnit Unit {
464                         get {
465                                 return _unit;
466                         }
467                 }
468
469                 public override bool Equals (object obj)
470                 {
471                         if (! (obj is Font))
472                                 return false;
473                                 
474                         Font fnt = (Font) obj;                  
475                         
476                         if (fnt.FontFamily.Equals (FontFamily) && fnt.Size == Size &&
477                             fnt.Style == Style && fnt.Unit == Unit &&
478                             fnt.GdiCharSet == GdiCharSet && 
479                             fnt.GdiVerticalFont == GdiVerticalFont)
480                                 return true;
481                         else
482                                 return false;
483                 }
484
485                 public override int GetHashCode ()
486                 {
487                         return _name.GetHashCode ();
488                 }
489
490                 [MonoTODO]
491                 public static Font FromHdc (IntPtr hdc)
492                 {
493                         throw new NotImplementedException ();
494                 }
495
496                 [MonoTODO("This is temporary implementation")]
497                 public static Font FromLogFont (object lf,  IntPtr hdc)
498                 {
499                         IntPtr newObject;
500                         LOGFONTA o = (LOGFONTA)lf;
501                         GDIPlus.GdipCreateFontFromLogfontA (hdc, ref o, out newObject);
502                         return new Font (newObject, "Microsoft Sans Serif", FontStyle.Regular, 10);
503                 }
504
505                 public float GetHeight ()
506                 {
507                         return GetHeight (Graphics.systemDpiY);
508                 }
509
510                 [MonoTODO]
511                 public static Font FromLogFont (object lf)
512                 {
513                         throw new NotImplementedException ();
514                 }
515
516                 public void ToLogFont (object logFont)
517                 {
518                         using (Graphics g = Graphics.FromHdc (GDIPlus.GetDC (IntPtr.Zero))) {
519                                 ToLogFont (logFont, g);
520                         }
521                 }
522
523                 public void ToLogFont (object logFont, Graphics graphics)
524                 {
525                         if (graphics == null) {
526                                 throw new ArgumentNullException ("graphics");
527                         }
528
529                         // TODO: Does it make a sense to deal with LOGFONTW ?
530                         LOGFONTA o = (LOGFONTA)logFont;
531                         GDIPlus.CheckStatus (GDIPlus.GdipGetLogFontA(NativeObject, graphics.NativeObject, ref o));
532                 }
533
534                 public float GetHeight (Graphics graphics)
535                 {
536                         float height = GetHeight (graphics.DpiY);
537
538                         switch (graphics.PageUnit) {
539                                 case GraphicsUnit.Document:
540                                         height *= (300f / graphics.DpiY);
541                                         break;
542                                 case GraphicsUnit.Display:
543                                         height *= (75f / graphics.DpiY);
544                                         break;
545                                 case GraphicsUnit.Inch:
546                                         height /=  graphics.DpiY;
547                                         break;
548                                 case GraphicsUnit.Millimeter:
549                                         height *= (25.4f / graphics.DpiY);
550                                         break;
551                                 case GraphicsUnit.Point:
552                                         height *= (72f / graphics.DpiY);
553                                         break;
554
555                                 case GraphicsUnit.Pixel:
556                                 case GraphicsUnit.World:
557                                 default:
558                                         break;
559                         }
560
561                         return height;
562                 }
563
564                 public float GetHeight (float dpi)
565                 {
566                         float height;
567                         int emHeight = _fontFamily.GetEmHeight (_style);
568                         int lineSpacing = _fontFamily.GetLineSpacing (_style);
569
570                         height = lineSpacing * (_size / emHeight);
571
572                         switch (_unit) {
573                                 case GraphicsUnit.Document:
574                                         height *= (dpi / 300f);
575                                         break;
576                                 case GraphicsUnit.Display:
577                                         height *= (dpi / 75f);
578                                         break;
579                                 case GraphicsUnit.Inch:
580                                         height *= dpi;
581                                         break;
582                                 case GraphicsUnit.Millimeter:
583                                         height *= (dpi / 25.4f);
584                                         break;
585                                 case GraphicsUnit.Point:
586                                         height *= (dpi / 72f);
587                                         break;
588
589                                 case GraphicsUnit.Pixel:
590                                 case GraphicsUnit.World:
591                                 default:
592                                         break;
593                         }
594
595                         return height;
596                 }
597
598                 public override String ToString ()
599                 {
600                         return String.Format ("[Font: Name={0}, Size={1}, Units={2}, GdiCharSet={3}, GdiVerticalFont={4}]", _name, _size, (int)_unit, _gdiCharSet, _gdiVerticalFont);
601                 }
602         }
603 }