Add tests for font measurement (#3538)

* Move WindowsFontQualityFromTextRenderingHint
* Add some basic tests for font measurement
* Remove WindowsFont.Size
This commit is contained in:
Jeremy Kuhne 2020-07-07 18:22:14 -07:00 коммит произвёл GitHub
Родитель 5f4586df51
Коммит 632aa7e96c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 360 добавлений и 291 удалений

Просмотреть файл

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Drawing.Text;
using static Interop;
namespace System.Windows.Forms

Просмотреть файл

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Drawing.Text;
using System.Windows.Forms.Internal;
using static Interop;
@ -14,119 +15,93 @@ namespace System.Windows.Forms
public static class TextRenderer
{
public static void DrawText(IDeviceContext dc, string? text, Font? font, Point pt, Color foreColor)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wg.DrawText(text, wf, pt, foreColor);
}
=> DrawTextInternal(dc, text, font, pt, foreColor);
public static void DrawText(IDeviceContext dc, string? text, Font? font, Point pt, Color foreColor, Color backColor)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
=> DrawTextInternal(dc, text, font, pt, foreColor, backColor);
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, pt, foreColor, flags: GetTextFormatFlags(flags));
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wg.DrawText(text, wf, pt, foreColor, backColor);
}
public static void DrawText(IDeviceContext dc, string? text, Font? font, Point pt, Color foreColor, TextFormatFlags flags)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var wgr = new WindowsGraphicsWrapper(dc, flags);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wgr.WindowsGraphics.DrawText(text, wf, pt, foreColor, GetTextFormatFlags(flags));
}
public static void DrawText(IDeviceContext dc, string? text, Font? font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var wgr = new WindowsGraphicsWrapper(dc, flags);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wgr.WindowsGraphics.DrawText(text, wf, pt, foreColor, backColor, GetTextFormatFlags(flags));
}
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, pt, foreColor, backColor, flags: GetTextFormatFlags(flags));
public static void DrawText(IDeviceContext dc, string? text, Font? font, Rectangle bounds, Color foreColor)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
=> DrawTextInternal(dc, text, font, bounds, foreColor);
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
public static void DrawText(
IDeviceContext dc,
string? text, Font?
font, Rectangle bounds,
Color foreColor,
Color backColor)
=> DrawTextInternal(dc, text, font, bounds, foreColor, backColor);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, bounds, foreColor, flags: GetTextFormatFlags(flags));
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, bounds, foreColor, backColor, flags: GetTextFormatFlags(flags));
private static void DrawTextInternal(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
Color backColor = default,
User32.DT flags = User32.DT.DEFAULT)
=> DrawTextInternal(dc, text, font, new Rectangle(pt, WindowsGraphics.MaxSize), foreColor, backColor, flags);
private static void DrawTextInternal(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor = default,
User32.DT flags = User32.DT.CENTER | User32.DT.VCENTER)
{
if (dc is null)
throw new ArgumentNullException(nameof(dc));
// Avoid creating the HDC, etc if we're not going to do any drawing
if (string.IsNullOrEmpty(text) || foreColor == Color.Transparent)
return;
// This MUST come before retreiving the HDC, which locks the Graphics object
Gdi32.QUALITY quality = FontQualityFromTextRenderingHint(dc);
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wg.DrawText(text, wf, bounds, foreColor);
}
public static void DrawText(IDeviceContext dc, string? text, Font? font, Rectangle bounds, Color foreColor, Color backColor)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wg.DrawText(text, wf, bounds, foreColor, backColor);
}
public static void DrawText(IDeviceContext dc, string? text, Font? font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var wgr = new WindowsGraphicsWrapper(dc, flags);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wgr.WindowsGraphics.DrawText(text, wf, bounds, foreColor, GetTextFormatFlags(flags));
}
public static void DrawText(IDeviceContext dc, string? text, Font? font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var wgr = new WindowsGraphicsWrapper(dc, flags);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
wgr.WindowsGraphics.DrawText(text, wf, bounds, foreColor, backColor, GetTextFormatFlags(flags));
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, quality);
wg.DrawText(text, wf, bounds, foreColor, backColor, flags);
}
private static User32.DT GetTextFormatFlags(TextFormatFlags flags)
@ -143,91 +118,59 @@ namespace System.Windows.Forms
}
public static Size MeasureText(string? text, Font? font)
{
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font);
return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf);
}
=> MeasureTextInternal(text, font, WindowsGraphics.MaxSize);
public static Size MeasureText(string? text, Font? font, Size proposedSize)
{
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font);
return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf, proposedSize);
}
=> MeasureTextInternal(text, font, proposedSize);
public static Size MeasureText(string? text, Font? font, Size proposedSize, TextFormatFlags flags)
=> MeasureTextInternal(text, font, proposedSize, flags);
public static Size MeasureText(IDeviceContext dc, string? text, Font? font)
=> MeasureTextInternal(dc, text, font, WindowsGraphics.MaxSize);
public static Size MeasureText(IDeviceContext dc, string? text, Font? font, Size proposedSize)
=> MeasureTextInternal(dc, text, font, proposedSize);
public static Size MeasureText(
IDeviceContext dc,
string? text,
Font? font,
Size proposedSize,
TextFormatFlags flags)
=> MeasureTextInternal(dc, text, font, proposedSize, flags);
private static Size MeasureTextInternal(
string? text,
Font? font,
Size proposedSize,
TextFormatFlags flags = TextFormatFlags.Bottom)
{
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font);
return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf, proposedSize, GetTextFormatFlags(flags));
}
public static Size MeasureText(IDeviceContext dc, string? text, Font? font)
private static Size MeasureTextInternal(
IDeviceContext dc,
string? text,
Font? font,
Size proposedSize,
TextFormatFlags flags = TextFormatFlags.Bottom)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
return wg.MeasureText(text, wf);
}
public static Size MeasureText(IDeviceContext dc, string? text, Font? font, Size proposedSize)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
using var hdc = new DeviceContextHdcScope(dc, applyGraphicsState: false);
using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
return wg.MeasureText(text, wf, proposedSize);
}
public static Size MeasureText(IDeviceContext dc, string? text, Font? font, Size proposedSize, TextFormatFlags flags)
{
if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}
if (string.IsNullOrEmpty(text))
{
return Size.Empty;
}
Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);
// This MUST come before retreiving the HDC, which locks the Graphics object
Gdi32.QUALITY quality = FontQualityFromTextRenderingHint(dc);
using var wgr = new WindowsGraphicsWrapper(dc, flags);
using var wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
using var wf = WindowsGraphicsCacheManager.GetWindowsFont(font, quality);
return wgr.WindowsGraphics.MeasureText(text, wf, proposedSize, GetTextFormatFlags(flags));
}
@ -238,14 +181,39 @@ namespace System.Windows.Forms
return SystemColors.GrayText;
}
//Theme specs -- if the backcolor is darker than Control, we use
// ControlPaint.Dark(backcolor). Otherwise we use ControlDark.
Color disabledTextForeColor = SystemColors.ControlDark;
if (ControlPaint.IsDarker(backColor, SystemColors.Control))
// If the color is darker than SystemColors.Control make it slightly darker,
// otherwise use the standard control dark color.
return ControlPaint.IsDarker(backColor, SystemColors.Control)
? ControlPaint.Dark(backColor)
: SystemColors.ControlDark;
}
/// <summary>
/// Attempts to match the TextRenderingHint of the specified Graphics object with a LOGFONT.lfQuality value.
/// </summary>
private static Gdi32.QUALITY FontQualityFromTextRenderingHint(IDeviceContext? deviceContext)
{
if (!(deviceContext is Graphics g))
{
disabledTextForeColor = ControlPaint.Dark(backColor);
return Gdi32.QUALITY.DEFAULT;
}
switch (g.TextRenderingHint)
{
case TextRenderingHint.ClearTypeGridFit:
return Gdi32.QUALITY.CLEARTYPE;
case TextRenderingHint.AntiAliasGridFit:
case TextRenderingHint.AntiAlias:
return Gdi32.QUALITY.ANTIALIASED;
case TextRenderingHint.SingleBitPerPixelGridFit:
return Gdi32.QUALITY.PROOF;
case TextRenderingHint.SingleBitPerPixel:
return Gdi32.QUALITY.DRAFT;
default:
case TextRenderingHint.SystemDefault:
return Gdi32.QUALITY.DEFAULT;
}
return disabledTextForeColor;
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Globalization;
using static Interop;
@ -15,7 +14,6 @@ namespace System.Windows.Forms.Internal
/// </summary>
internal sealed partial class WindowsFont : ICloneable, IDisposable
{
private float _fontSize = -1.0f; // invalid value.
private int _lineSpacing;
private bool _ownHandle;
private bool _ownedByCacheManager;
@ -226,7 +224,7 @@ namespace System.Windows.Forms.Internal
/// <summary>
/// Gets the hash code for this WindowsFont.
/// </summary>
public override int GetHashCode() => HashCode.Combine(Style, CharSet, Size);
public override int GetHashCode() => HashCode.Combine(Style, CharSet, LogFontHeight);
/// <summary>
/// Clones this object.
@ -238,7 +236,10 @@ namespace System.Windows.Forms.Internal
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "[{0}: Name={1}, Size={2} points, Height={3} pixels, Sytle={4}]", GetType().Name, _logFont.FaceName.ToString(), Size, Height, Style);
return string.Format(
CultureInfo.CurrentCulture,
"[{0}: Name={1}, lfHeight={2}, Height={3} pixels, Sytle={4}]",
GetType().Name, _logFont.FaceName.ToString(), LogFontHeight, Height, Style);
}
////////////////////////////////////////////
@ -338,63 +339,5 @@ namespace System.Windows.Forms.Internal
/// The font's face name.
/// </summary>
public string Name => _logFont.FaceName.ToString();
/// <summary>
/// Gets the character height (as opposed to the cell height) of the font represented by this object in points.
/// Consider
/// </summary>
public float Size
{
get
{
if (_fontSize < 0.0f)
{
WindowsGraphics wg = WindowsGraphicsCacheManager.MeasurementGraphics;
// No need to reset the font (if changed) since we always set the font before using the MeasurementGraphics
// in WindowsGraphics methods.
wg.DeviceContext.SelectFont(this);
Gdi32.TEXTMETRICW tm = wg.GetTextMetrics();
// Convert the font character height to points. If lfHeight is negative, Windows
// treats the absolute value of that number as a desired font height compatible with
// the point size; in this case lfHeight will roughly match the tmHeight field of
// the TEXTMETRIC structure less the tmInternalLeading field.
int height = _logFont.lfHeight > 0 ? tm.tmHeight : (tm.tmHeight - tm.tmInternalLeading);
_fontSize = height * 72f / wg.DeviceContext.DpiY;
}
return _fontSize;
}
}
/// <summary>
/// Attempts to match the TextRenderingHint of the specified Graphics object with a LOGFONT.lfQuality value.
/// </summary>
public static Gdi32.QUALITY WindowsFontQualityFromTextRenderingHint(Graphics? g)
{
if (g == null)
{
return Gdi32.QUALITY.DEFAULT;
}
switch (g.TextRenderingHint)
{
case TextRenderingHint.ClearTypeGridFit:
return Gdi32.QUALITY.CLEARTYPE;
case TextRenderingHint.AntiAliasGridFit:
case TextRenderingHint.AntiAlias:
return Gdi32.QUALITY.ANTIALIASED;
case TextRenderingHint.SingleBitPerPixelGridFit:
return Gdi32.QUALITY.PROOF;
case TextRenderingHint.SingleBitPerPixel:
return Gdi32.QUALITY.DRAFT;
default:
case TextRenderingHint.SystemDefault:
return Gdi32.QUALITY.DEFAULT;
}
}
}
}

Просмотреть файл

@ -71,46 +71,6 @@ namespace System.Windows.Forms.Internal
public void DrawAndFillEllipse(WindowsPen pen, WindowsBrush brush, Rectangle bounds)
=> DrawEllipse(pen, brush, bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
/// <summary>
/// Draws the text at the specified point, using the given Font and foreColor.
/// CR/LF are honored.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Point pt, Color foreColor)
=> DrawText(text, font, pt, foreColor, Color.Empty, User32.DT.DEFAULT);
/// <summary>
/// Draws the text at the specified point, using the given Font, foreColor and backColor.
/// CR/LF are honored.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Point pt, Color foreColor, Color backColor)
=> DrawText(text, font, pt, foreColor, backColor, User32.DT.DEFAULT);
/// <summary>
/// Draws the text at the specified point, using the given Font and foreColor, and according to the
/// specified flags.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Point pt, Color foreColor, User32.DT flags)
=> DrawText(text, font, pt, foreColor, Color.Empty, flags);
/// <summary>
/// Draws the text at the specified point, using the given Font, foreColor and backColor, and according
/// to the specified flags.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Point pt, Color foreColor, Color backColor, User32.DT flags)
=> DrawText(text, font, new Rectangle(pt, MaxSize), foreColor, backColor, flags);
/// <summary>
/// Draws the text centered in the given rectangle and using the given Font and foreColor.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Rectangle bounds, Color foreColor)
=> DrawText(text, font, bounds, foreColor, Color.Empty);
/// <summary>
/// Draws the text centered in the given rectangle and using the given Font, foreColor and backColor.
/// </summary>
public void DrawText(string? text, WindowsFont? font, Rectangle bounds, Color foreColor, Color backColor)
=> DrawText(text, font, bounds, foreColor, backColor, User32.DT.CENTER | User32.DT.VCENTER);
/// <summary>
/// Draws the text in the given bounds, using the given Font and foreColor, and according to the specified flags.
/// </summary>
@ -283,21 +243,6 @@ namespace System.Windows.Forms.Internal
return new Size(size.Width, size.Height);
}
/// <summary>
/// Returns the Size in logical units of the given text using the given Font.
/// CR/LF/TAB are taken into account.
/// </summary>
public Size MeasureText(string? text, WindowsFont? font)
=> MeasureText(text, font, MaxSize, User32.DT.BOTTOM);
/// <summary>
/// Returns the Size in logical units of the given text using the given Font and using the specified rectangle
/// as the text bounding box (see overload below for more info).
/// TAB/CR/LF are taken into account.
/// </summary>
public Size MeasureText(string? text, WindowsFont? font, Size proposedSize)
=> MeasureText(text, font, proposedSize, User32.DT.BOTTOM);
/// <summary>
/// Returns the Size in logical units of the given text using the given Font, and according to the formatting flags.
/// The proposed size is used to create a bounding rectangle as follows:

Просмотреть файл

@ -0,0 +1,212 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Windows.Forms.Internal;
using Xunit;
using static Interop;
namespace System.Windows.Forms.Tests.Text
{
public class FontMetrics
{
[Theory]
[InlineData("Arial", 9.0f, 15)]
[InlineData("Arial", 12.0f, 18)]
[InlineData("Microsoft Sans Serif", 16.0f, 26)]
[InlineData("Times New Roman", 11.0f, 17)]
[InlineData("MS Gothic", 10.0f, 14)]
public void Font_GetHeight(string family, float size, int height)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
Assert.Equal(height, windowsFont.Height);
}
[Theory]
[InlineData("Arial", 9.0f, 1 /* DEFAULT_CHARSET */)]
[InlineData("Arial", 12.0f, 1)]
[InlineData("Microsoft Sans Serif", 16.0f, 1)]
[InlineData("Times New Roman", 11.0f, 1)]
[InlineData("MS Gothic", 11.0f, 1)]
public void Font_GetCharSet(string family, float size, byte charset)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
Assert.Equal(charset, windowsFont.CharSet);
}
[Theory]
[InlineData("Arial", 9.0f, 2.5f)]
[InlineData("Arial", 12.0f, 3.0f)]
[InlineData("Microsoft Sans Serif", 16.0f, 4.3333335f)]
[InlineData("Times New Roman", 11.0f, 2.8333333f)]
[InlineData("MS Gothic", 10.0f, 2.3333333f)]
public void Font_GetOverhangPadding(string family, float size, float expected)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
WindowsGraphics graphics = WindowsGraphicsCacheManager.MeasurementGraphics;
Assert.Equal(expected, graphics.GetOverhangPadding(windowsFont));
}
[Theory]
[InlineData("Arial", 9.0f, 3, 4)]
[InlineData("Arial", 12.0f, 3, 5)]
[InlineData("Microsoft Sans Serif", 16.0f, 5, 7)]
[InlineData("Times New Roman", 11.0f, 3, 5)]
[InlineData("MS Gothic", 10.0f, 3, 4)]
public void Font_GetTextMargins(string family, float size, int left, int right)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
WindowsGraphics graphics = WindowsGraphicsCacheManager.MeasurementGraphics;
User32.DRAWTEXTPARAMS margins = graphics.GetTextMargins(windowsFont);
Assert.Equal(left, margins.iLeftMargin);
Assert.Equal(right, margins.iRightMargin);
}
[Theory]
[InlineData("Arial", 9.0f, 73, 15)]
[InlineData("Arial", 12.0f, 95, 18)]
[InlineData("Microsoft Sans Serif", 16.0f, 136, 26)]
[InlineData("Times New Roman", 11.0f, 84, 17)]
[InlineData("MS Gothic", 10.0f, 91, 14)]
public void Font_GetTextExtent(string family, float size, int width, int height)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
WindowsGraphics graphics = WindowsGraphicsCacheManager.MeasurementGraphics;
Size extent = graphics.GetTextExtent("Whizzo Butter", windowsFont);
Assert.Equal(width, extent.Width);
Assert.Equal(height, extent.Height);
}
[Theory]
[MemberData(nameof(MeasureTextData))]
public void Font_MeasureText(string family, float size, Size proposedSize, uint dt, Size expected)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
WindowsGraphics graphics = WindowsGraphicsCacheManager.MeasurementGraphics;
Size measure = graphics.MeasureText("Windows Foundation Classes", windowsFont, proposedSize, (User32.DT)dt);
Assert.Equal(expected, measure);
}
public static TheoryData<string, float, Size, uint, Size> MeasureTextData =>
new TheoryData<string, float, Size, uint, Size>
{
{ "Arial", 9.0f, new Size(-1, -1), (uint)User32.DT.BOTTOM, new Size(173, 15) },
{ "Arial", 12.0f, new Size(-1, -1), (uint)User32.DT.BOTTOM, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(-1, -1), (uint)User32.DT.BOTTOM, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(-1, -1), (uint)User32.DT.BOTTOM, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(-1, -1), (uint)User32.DT.BOTTOM, new Size(189, 14) },
{ "Arial", 9.0f, new Size(0, 0), (uint)User32.DT.BOTTOM, new Size(173, 15) },
{ "Arial", 12.0f, new Size(0, 0), (uint)User32.DT.BOTTOM, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(0, 0), (uint)User32.DT.BOTTOM, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(0, 0), (uint)User32.DT.BOTTOM, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(0, 0), (uint)User32.DT.BOTTOM, new Size(189, 14) },
{ "Arial", 9.0f, new Size(1, 1), (uint)User32.DT.BOTTOM, new Size(173, 15) },
{ "Arial", 12.0f, new Size(1, 1), (uint)User32.DT.BOTTOM, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(1, 1), (uint)User32.DT.BOTTOM, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(1, 1), (uint)User32.DT.BOTTOM, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(1, 1), (uint)User32.DT.BOTTOM, new Size(189, 14) },
{ "Arial", 9.0f, new Size(300, 300), (uint)User32.DT.BOTTOM, new Size(173, 15) },
{ "Arial", 12.0f, new Size(300, 300), (uint)User32.DT.BOTTOM, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(300, 300), (uint)User32.DT.BOTTOM, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(300, 300), (uint)User32.DT.BOTTOM, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(300, 300), (uint)User32.DT.BOTTOM, new Size(189, 14) },
{ "Arial", 9.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.BOTTOM, new Size(173, 15) },
{ "Arial", 12.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.BOTTOM, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.BOTTOM, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.BOTTOM, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.BOTTOM, new Size(189, 14) },
{ "Arial", 9.0f, new Size(1, 1), (uint)User32.DT.SINGLELINE, new Size(173, 15) },
{ "Arial", 12.0f, new Size(1, 1), (uint)User32.DT.SINGLELINE, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(1, 1), (uint)User32.DT.SINGLELINE, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(1, 1), (uint)User32.DT.SINGLELINE, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(1, 1), (uint)User32.DT.SINGLELINE, new Size(189, 14) },
{ "Arial", 9.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.SINGLELINE, new Size(173, 15) },
{ "Arial", 12.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.SINGLELINE, new Size(215, 18) },
{ "Microsoft Sans Serif", 16.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.SINGLELINE, new Size(299, 26) },
{ "Times New Roman", 11.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.SINGLELINE, new Size(179, 17) },
{ "MS Gothic", 10.0f, new Size(int.MaxValue, int.MaxValue), (uint)User32.DT.SINGLELINE, new Size(189, 14) },
};
[Theory]
[MemberData(nameof(AdjustData))]
public void Font_AdjustForVerticalAlignment(string family, float size, Rectangle bounds, uint dt, Rectangle expected)
{
using Font font = new Font(family, size);
if (font.Name != family)
{
// Not installed on this machine
return;
}
using WindowsFont windowsFont = WindowsFont.FromFont(font, Gdi32.QUALITY.CLEARTYPE);
WindowsGraphics graphics = WindowsGraphicsCacheManager.MeasurementGraphics;
graphics.DeviceContext.SelectFont(windowsFont);
User32.DRAWTEXTPARAMS param = default;
Rectangle result = WindowsGraphics.AdjustForVerticalAlignment(
graphics,
"Windows Foundation Classes",
bounds,
(User32.DT)dt,
ref param);
Assert.Equal(expected, result);
}
public static TheoryData<string, float, Rectangle, uint, Rectangle> AdjustData =>
new TheoryData<string, float, Rectangle, uint, Rectangle>
{
{ "Arial", 9.0f, new Rectangle(1, 1, 1, 1), (uint)User32.DT.BOTTOM, new Rectangle(1, 1, 1, 1) },
{ "Arial", 12.0f, new Rectangle(1, 1, 1, 1), (uint)User32.DT.BOTTOM, new Rectangle(1, 1, 1, 1) },
{ "Microsoft Sans Serif", 16.0f, new Rectangle(1, 1, 1, 1), (uint)User32.DT.BOTTOM, new Rectangle(1, 1, 1, 1) },
{ "Times New Roman", 11.0f, new Rectangle(1, 1, 1, 1), (uint)User32.DT.BOTTOM, new Rectangle(1, 1, 1, 1) },
{ "MS Gothic", 10.0f, new Rectangle(1, 1, 1, 1), (uint)User32.DT.BOTTOM, new Rectangle(1, 1, 1, 1) },
{ "Arial", 9.0f, new Rectangle(1, 1, 100, 100), (uint)User32.DT.BOTTOM, new Rectangle(1, 86, 100, 100) },
{ "Arial", 12.0f, new Rectangle(1, 1, 100, 100), (uint)User32.DT.BOTTOM, new Rectangle(1, 83, 100, 100) },
{ "Microsoft Sans Serif", 16.0f, new Rectangle(1, 1, 100, 100), (uint)User32.DT.BOTTOM, new Rectangle(1, 75, 100, 100) },
{ "Times New Roman", 11.0f, new Rectangle(1, 1, 100, 100), (uint)User32.DT.BOTTOM, new Rectangle(1, 84, 100, 100) },
{ "MS Gothic", 10.0f, new Rectangle(1, 1, 100, 100), (uint)User32.DT.BOTTOM, new Rectangle(1, 87, 100, 100) },
};
}
}