Font
public sealed class Font : MarshalByRefObject, ICloneable, IDisposable, ISerializable, IPointer<GpFont>
Defines a particular format for text, including font face, size, and style attributes. This class cannot be inherited.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Interop;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.Graphics.GdiPlus;
namespace System.Drawing
{
[Serializable]
[NullableContext(1)]
[Nullable(0)]
[Editor("System.Drawing.Design.FontEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[TypeConverter(typeof(FontConverter))]
[TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public sealed class Font : MarshalByRefObject, ICloneable, IDisposable, ISerializable, IPointer<GpFont>
{
[NonSerialized]
[Nullable(0)]
private unsafe GpFont* _nativeFont;
private float _fontSize;
private FontStyle _fontStyle;
private FontFamily _fontFamily;
private GraphicsUnit _fontUnit;
private byte _gdiCharSet = 1;
private bool _gdiVerticalFont;
private string _systemFontName = string.Empty;
[Nullable(2)]
private string _originalFontName;
unsafe IntPtr IPointer<GpFont>.Pointer {
get {
return (IntPtr)_nativeFont;
}
}
public float Size => _fontSize;
[Browsable(false)]
public FontStyle Style {
get {
return _fontStyle;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool Bold {
get {
return (Style & FontStyle.Bold) != FontStyle.Regular;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool Italic {
get {
return (Style & FontStyle.Italic) != FontStyle.Regular;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool Strikeout {
get {
return (Style & FontStyle.Strikeout) != FontStyle.Regular;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool Underline {
get {
return (Style & FontStyle.Underline) != FontStyle.Regular;
}
}
[Browsable(false)]
public FontFamily FontFamily {
get {
return _fontFamily;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Editor("System.Drawing.Design.FontNameEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[TypeConverter(typeof(FontConverter.FontNameConverter))]
public string Name {
get {
return FontFamily.Name;
}
}
[TypeConverter(typeof(FontConverter.FontUnitConverter))]
public GraphicsUnit Unit {
get {
return _fontUnit;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public byte GdiCharSet {
get {
return _gdiCharSet;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool GdiVerticalFont {
get {
return _gdiVerticalFont;
}
}
[Nullable(2)]
[Browsable(false)]
public string OriginalFontName {
[NullableContext(2)]
get {
return _originalFontName;
}
}
[Browsable(false)]
public string SystemFontName {
get {
return _systemFontName;
}
}
[Browsable(false)]
public bool IsSystemFont {
get {
return !string.IsNullOrEmpty(_systemFontName);
}
}
[Browsable(false)]
public int Height {
get {
return (int)Math.Ceiling((double)GetHeight());
}
}
[Nullable(0)]
internal unsafe GpFont* NativeFont {
[NullableContext(0)]
get {
return _nativeFont;
}
}
[Browsable(false)]
public float SizeInPoints {
get {
if (Unit != GraphicsUnit.Point) {
GetDcScope scope = GetDcScope.ScreenDC;
try {
using (Graphics graphics = Graphics.FromHdcInternal((IntPtr)ref scope)) {
float num = (float)((double)graphics.DpiY / 72);
return GetHeight(graphics) * (float)FontFamily.GetEmHeight(Style) / (float)FontFamily.GetLineSpacing(Style) / num;
}
} finally {
scope.Dispose();
}
}
return Size;
}
}
~Font()
{
Dispose(false);
}
private Font(SerializationInfo info, StreamingContext context)
{
string string = info.GetString("Name");
FontStyle style = (FontStyle)info.GetValue("Style", typeof(FontStyle));
GraphicsUnit unit = (GraphicsUnit)info.GetValue("Unit", typeof(GraphicsUnit));
float single = info.GetSingle("Size");
Initialize(string, single, style, unit, 1, IsVerticalName(string));
}
void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
{
string value = string.IsNullOrEmpty(OriginalFontName) ? Name : OriginalFontName;
si.AddValue("Name", value);
si.AddValue("Size", Size);
si.AddValue("Style", Style);
si.AddValue("Unit", Unit);
}
private static bool IsVerticalName(string familyName)
{
if (familyName != null && familyName.Length > 0)
return familyName[0] == '@';
return false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private unsafe void Dispose(bool disposing)
{
if (_nativeFont != null)
try {
PInvokeGdiPlus.GdipDeleteFont(_nativeFont);
} catch (Exception ex) when (!ClientUtils.IsCriticalException(ex)) {
} finally {
_nativeFont = null;
}
}
public unsafe float GetHeight(Graphics graphics)
{
ArgumentNullException.ThrowIfNull(graphics, "graphics");
if (graphics.NativeGraphics == null)
throw new ArgumentException(null, "graphics");
float result = default(float);
PInvokeGdiPlus.GdipGetFontHeight(NativeFont, graphics.Pointer(), &result).ThrowIfFailed();
GC.KeepAlive(this);
GC.KeepAlive(graphics);
return result;
}
public unsafe float GetHeight(float dpi)
{
float result = default(float);
PInvokeGdiPlus.GdipGetFontHeightGivenDPI(NativeFont, dpi, &result).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
[NullableContext(2)]
public override bool Equals([NotNullWhen(true)] object obj)
{
if (obj == this)
return true;
Font font = obj as Font;
if (font == null)
return false;
if (font.FontFamily.Equals(FontFamily) && font.GdiVerticalFont == GdiVerticalFont && font.GdiCharSet == GdiCharSet && font.Style == Style && font.Size == Size)
return font.Unit == Unit;
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(Name, Style, Size, Unit);
}
public override string ToString()
{
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 6);
defaultInterpolatedStringHandler.AppendLiteral("[");
defaultInterpolatedStringHandler.AppendFormatted(GetType().Name);
defaultInterpolatedStringHandler.AppendLiteral(": Name=");
defaultInterpolatedStringHandler.AppendFormatted(FontFamily.Name);
defaultInterpolatedStringHandler.AppendLiteral(", Size=");
defaultInterpolatedStringHandler.AppendFormatted(_fontSize);
defaultInterpolatedStringHandler.AppendLiteral(", Units=");
defaultInterpolatedStringHandler.AppendFormatted((int)_fontUnit);
defaultInterpolatedStringHandler.AppendLiteral(", GdiCharSet=");
defaultInterpolatedStringHandler.AppendFormatted(_gdiCharSet);
defaultInterpolatedStringHandler.AppendLiteral(", GdiVerticalFont=");
defaultInterpolatedStringHandler.AppendFormatted(_gdiVerticalFont);
defaultInterpolatedStringHandler.AppendLiteral("]");
return defaultInterpolatedStringHandler.ToStringAndClear();
}
internal void SetSystemFontName(string systemFontName)
{
_systemFontName = systemFontName;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public unsafe void ToLogFont(object logFont, Graphics graphics)
{
ArgumentNullException.ThrowIfNull(logFont, "logFont");
Type type = logFont.GetType();
int num = sizeof(LOGFONT);
if (Marshal.SizeOf(type) != num)
throw new ArgumentException(null, "logFont");
ToLogFont(out LOGFONT logFont2, graphics);
if (!type.IsValueType)
Marshal.PtrToStructure(new IntPtr(&logFont2), logFont);
else {
GCHandle gCHandle = GCHandle.Alloc(logFont, GCHandleType.Pinned);
Buffer.MemoryCopy(&logFont2, (void*)(long)gCHandle.AddrOfPinnedObject(), num, num);
gCHandle.Free();
}
}
public unsafe void ToLogFont(out LOGFONT logFont, Graphics graphics)
{
ArgumentNullException.ThrowIfNull(graphics, "graphics");
fixed (LOGFONT* logfontW = &logFont) {
PInvokeGdiPlus.GdipGetLogFont(NativeFont, graphics.Pointer(), (LOGFONTW*)logfontW).ThrowIfFailed();
GC.KeepAlive(this);
GC.KeepAlive(graphics);
}
if (_gdiVerticalFont) {
Span<char> lfFaceName = logFont.lfFaceName;
lfFaceName.Slice(0, lfFaceName.Length - 1).CopyTo(lfFaceName.Slice(1, lfFaceName.Length - 1));
lfFaceName[0] = '@';
lfFaceName[lfFaceName.Length - 1] = ' ';
}
if (logFont.lfCharSet == 0)
logFont.lfCharSet = _gdiCharSet;
}
private unsafe void CreateNativeFont()
{
GpFont* nativeFont = default(GpFont*);
Status status = PInvokeGdiPlus.GdipCreateFont(_fontFamily.Pointer(), _fontSize, (int)_fontStyle, (Unit)_fontUnit, &nativeFont);
GC.KeepAlive(this);
_nativeFont = nativeFont;
switch (status) {
case Status.Ok:
break;
case Status.FontStyleNotFound:
throw new ArgumentException(System.SR.Format(System.SR.GdiplusFontStyleNotFound, _fontFamily.Name, _fontStyle.ToString()));
default:
throw status.GetException();
}
}
public Font(Font prototype, FontStyle newStyle)
{
_originalFontName = prototype.OriginalFontName;
Initialize(prototype.FontFamily, prototype.Size, newStyle, prototype.Unit, 1, false);
}
public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
{
Initialize(family, emSize, style, unit, 1, false);
}
public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)
{
Initialize(family, emSize, style, unit, gdiCharSet, false);
}
public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
{
Initialize(family, emSize, style, unit, gdiCharSet, gdiVerticalFont);
}
public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)
{
Initialize(familyName, emSize, style, unit, gdiCharSet, IsVerticalName(familyName));
}
public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
{
if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0)
throw new ArgumentException(System.SR.Format(System.SR.InvalidBoundArgument, "emSize", emSize, 0, "System.Single.MaxValue"), "emSize");
Initialize(familyName, emSize, style, unit, gdiCharSet, gdiVerticalFont);
}
public Font(FontFamily family, float emSize, FontStyle style)
{
Initialize(family, emSize, style, GraphicsUnit.Point, 1, false);
}
public Font(FontFamily family, float emSize, GraphicsUnit unit)
{
Initialize(family, emSize, FontStyle.Regular, unit, 1, false);
}
public Font(FontFamily family, float emSize)
{
Initialize(family, emSize, FontStyle.Regular, GraphicsUnit.Point, 1, false);
}
public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit)
{
Initialize(familyName, emSize, style, unit, 1, IsVerticalName(familyName));
}
public Font(string familyName, float emSize, FontStyle style)
{
Initialize(familyName, emSize, style, GraphicsUnit.Point, 1, IsVerticalName(familyName));
}
public Font(string familyName, float emSize, GraphicsUnit unit)
{
Initialize(familyName, emSize, FontStyle.Regular, unit, 1, IsVerticalName(familyName));
}
public Font(string familyName, float emSize)
{
Initialize(familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, 1, IsVerticalName(familyName));
}
[NullableContext(0)]
private unsafe Font(GpFont* nativeFont, byte gdiCharSet, bool gdiVerticalFont)
{
_nativeFont = nativeFont;
GraphicsUnit unit = default(GraphicsUnit);
PInvokeGdiPlus.GdipGetFontUnit(_nativeFont, (Unit*)(&unit)).ThrowIfFailed();
float emSize = default(float);
PInvokeGdiPlus.GdipGetFontSize(_nativeFont, &emSize).ThrowIfFailed();
FontStyle style = default(FontStyle);
PInvokeGdiPlus.GdipGetFontStyle(_nativeFont, (int*)(&style)).ThrowIfFailed();
GpFontFamily* family = default(GpFontFamily*);
PInvokeGdiPlus.GdipGetFamily(_nativeFont, &family).ThrowIfFailed();
SetFontFamily(new FontFamily(family, true));
Initialize(_fontFamily, emSize, style, unit, gdiCharSet, gdiVerticalFont);
}
private void Initialize(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
{
_originalFontName = familyName;
ReadOnlySpan<char> name = familyName.AsSpan();
if (name.Length > 1 && name[0] == '@')
name = name.Slice(1, name.Length - 1);
SetFontFamily(new FontFamily(name, true));
Initialize(_fontFamily, emSize, style, unit, gdiCharSet, gdiVerticalFont);
}
private unsafe void Initialize(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
{
ArgumentNullException.ThrowIfNull(family, "family");
if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0)
throw new ArgumentException(System.SR.Format(System.SR.InvalidBoundArgument, "emSize", emSize, 0, "System.Single.MaxValue"), "emSize");
_fontSize = emSize;
_fontStyle = style;
_fontUnit = unit;
_gdiCharSet = gdiCharSet;
_gdiVerticalFont = gdiVerticalFont;
if (_fontFamily == null)
SetFontFamily(family.Clone());
if (_nativeFont == null)
CreateNativeFont();
float fontSize = default(float);
Status status = PInvokeGdiPlus.GdipGetFontSize(_nativeFont, &fontSize);
_fontSize = fontSize;
GC.KeepAlive(this);
Gdip.CheckStatus(status);
}
public static Font FromHfont(IntPtr hfont)
{
PInvokeCore.GetObject((HGDIOBJ)hfont, out LOGFONT object);
GetDcScope scope = GetDcScope.ScreenDC;
try {
return FromLogFont(ref object, (IntPtr)ref scope);
} finally {
scope.Dispose();
}
}
public static Font FromLogFont(object lf)
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
return FromLogFont(lf, (IntPtr)ref scope);
} finally {
scope.Dispose();
}
}
public static Font FromLogFont([In] [IsReadOnly] ref LOGFONT logFont)
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
return FromLogFont(ref logFont, (IntPtr)ref scope);
} finally {
scope.Dispose();
}
}
public unsafe static Font FromLogFont([In] [IsReadOnly] ref LOGFONT logFont, IntPtr hdc)
{
Status status;
GpFont* ptr = default(GpFont*);
fixed (LOGFONT* logfont = &logFont) {
status = PInvokeGdiPlus.GdipCreateFontFromLogfont((HDC)hdc, (LOGFONTW*)logfont, &ptr);
}
switch (status) {
case Status.NotTrueTypeFont:
throw new ArgumentException(System.SR.GdiplusNotTrueTypeFont_NoName);
default:
throw Gdip.StatusException(status);
case Status.Ok:
if (ptr == null)
throw new ArgumentException(System.SR.Format(System.SR.GdiplusNotTrueTypeFont, logFont.AsString()));
return new Font(ptr, logFont.lfCharSet, logFont.IsGdiVerticalFont);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public unsafe static Font FromLogFont(object lf, IntPtr hdc)
{
ArgumentNullException.ThrowIfNull(lf, "lf");
LOGFONT logFont;
if (lf is LOGFONT) {
logFont = (LOGFONT)lf;
return FromLogFont(ref logFont, hdc);
}
Type type = lf.GetType();
int num = sizeof(LOGFONT);
if (Marshal.SizeOf(type) != num)
throw new ArgumentException(null, "lf");
logFont = default(LOGFONT);
Marshal.StructureToPtr(lf, new IntPtr(&logFont), false);
return FromLogFont(ref logFont, hdc);
}
public unsafe static Font FromHdc(IntPtr hdc)
{
GpFont* nativeFont = default(GpFont*);
Status status = PInvokeGdiPlus.GdipCreateFontFromDC((HDC)hdc, &nativeFont);
switch (status) {
case Status.NotTrueTypeFont:
throw new ArgumentException(System.SR.GdiplusNotTrueTypeFont_NoName);
default:
throw Gdip.StatusException(status);
case Status.Ok:
return new Font(nativeFont, 0, false);
}
}
public unsafe object Clone()
{
GpFont* nativeFont = default(GpFont*);
PInvokeGdiPlus.GdipCloneFont(_nativeFont, &nativeFont).ThrowIfFailed();
GC.KeepAlive(this);
return new Font(nativeFont, _gdiCharSet, _gdiVerticalFont);
}
private void SetFontFamily(FontFamily family)
{
_fontFamily = family;
GC.SuppressFinalize(_fontFamily);
}
public void ToLogFont(object logFont)
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
using (Graphics graphics = Graphics.FromHdcInternal((IntPtr)ref scope))
ToLogFont(logFont, graphics);
} finally {
scope.Dispose();
}
}
public void ToLogFont(out LOGFONT logFont)
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
using (Graphics graphics = Graphics.FromHdcInternal((IntPtr)ref scope))
ToLogFont(out logFont, graphics);
} finally {
scope.Dispose();
}
}
public unsafe IntPtr ToHfont()
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
using (Graphics graphics = Graphics.FromHdcInternal((IntPtr)ref scope)) {
ToLogFont(out LOGFONT logFont, graphics);
HFONT value = PInvokeCore.CreateFontIndirect((LOGFONTW*)(&logFont));
if (value.IsNull)
throw new Win32Exception();
return value;
}
} finally {
scope.Dispose();
}
}
public float GetHeight()
{
GetDcScope scope = GetDcScope.ScreenDC;
try {
using (Graphics graphics = Graphics.FromHdcInternal((IntPtr)ref scope))
return GetHeight(graphics);
} finally {
scope.Dispose();
}
}
}
}