diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Gdi32/Interop.HENHMETAFILE.cs b/src/System.Windows.Forms.Primitives/src/Interop/Gdi32/Interop.HENHMETAFILE.cs
index 9d6d0fe59ea..bcd21574b5b 100644
--- a/src/System.Windows.Forms.Primitives/src/Interop/Gdi32/Interop.HENHMETAFILE.cs
+++ b/src/System.Windows.Forms.Primitives/src/Interop/Gdi32/Interop.HENHMETAFILE.cs
@@ -17,6 +17,7 @@ public readonly struct HENHMETAFILE
public bool IsNull => Handle == IntPtr.Zero;
public static explicit operator IntPtr(HENHMETAFILE hmetafile) => hmetafile.Handle;
+ public static explicit operator HENHMETAFILE(IntPtr hmetafile) => new HENHMETAFILE(hmetafile);
}
}
}
diff --git a/src/System.Windows.Forms.Primitives/tests/TestUtilities/Metafiles/EmfScope.cs b/src/System.Windows.Forms.Primitives/tests/TestUtilities/Metafiles/EmfScope.cs
index 11db6443b6a..100bc64f55b 100644
--- a/src/System.Windows.Forms.Primitives/tests/TestUtilities/Metafiles/EmfScope.cs
+++ b/src/System.Windows.Forms.Primitives/tests/TestUtilities/Metafiles/EmfScope.cs
@@ -16,7 +16,7 @@ namespace System.Windows.Forms.Metafiles
internal class EmfScope : DisposalTracking.Tracker, IDisposable
{
public Gdi32.HDC HDC { get; }
- private Gdi32.HENHMETAFILE _hmf;
+ private Gdi32.HENHMETAFILE _hemf;
public unsafe EmfScope()
: this (CreateEnhMetaFile())
@@ -26,7 +26,12 @@ public unsafe EmfScope()
public EmfScope(Gdi32.HDC hdc)
{
HDC = hdc;
- _hmf = default;
+ _hemf = default;
+ }
+
+ public EmfScope(Gdi32.HENHMETAFILE hemf)
+ {
+ _hemf = hemf;
}
private unsafe static Gdi32.HDC CreateEnhMetaFile(
@@ -49,17 +54,17 @@ public Gdi32.HENHMETAFILE HENHMETAFILE
{
get
{
- if (HDC.IsNull)
+ if (_hemf.IsNull)
{
- return default;
- }
+ if (HDC.IsNull)
+ {
+ return default;
+ }
- if (_hmf.IsNull)
- {
- _hmf = Gdi32.CloseEnhMetaFile(HDC);
+ _hemf = Gdi32.CloseEnhMetaFile(HDC);
}
- return _hmf;
+ return _hemf;
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/TextRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/TextRenderer.cs
index 5ceb21785d3..d05a1e8f571 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/TextRenderer.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/TextRenderer.cs
@@ -300,7 +300,7 @@ internal static void DrawTextInternal(
// This MUST come before retreiving the HDC, which locks the Graphics object
Gdi32.QUALITY quality = FontQualityFromTextRenderingHint(dc);
- using var hdc = new DeviceContextHdcScope(dc);
+ using var hdc = new DeviceContextHdcScope(dc, GetApplyStateFlags(dc, flags));
DrawTextInternal(hdc, text, font, bounds, foreColor, quality, backColor, flags);
}
@@ -533,7 +533,9 @@ private static Size MeasureTextInternal(
// This MUST come before retreiving the HDC, which locks the Graphics object
Gdi32.QUALITY quality = FontQualityFromTextRenderingHint(dc);
- using var hdc = new DeviceContextHdcScope(dc);
+ // Applying state may not impact text size measurements. Rather than risk missing some
+ // case we'll apply as we have historically to avoid suprise regressions.
+ using var hdc = new DeviceContextHdcScope(dc, GetApplyStateFlags(dc, flags));
using var hfont = GdiCache.GetHFONT(font, quality, hdc);
return hdc.HDC.MeasureText(text, hfont, proposedSize, flags);
}
@@ -595,5 +597,29 @@ private static Gdi32.QUALITY GetDefaultFontQuality()
return SystemInformation.FontSmoothingType == 0x0002
? Gdi32.QUALITY.CLEARTYPE : Gdi32.QUALITY.ANTIALIASED;
}
+
+ ///
+ /// Gets the proper flags for the given .
+ ///
+ private static ApplyGraphicsProperties GetApplyStateFlags(IDeviceContext deviceContext, TextFormatFlags textFormatFlags)
+ {
+ if (deviceContext is not Graphics)
+ {
+ return ApplyGraphicsProperties.None;
+ }
+
+ var apply = ApplyGraphicsProperties.None;
+ if (textFormatFlags.HasFlag(TextFormatFlags.PreserveGraphicsClipping))
+ {
+ apply |= ApplyGraphicsProperties.Clipping;
+ }
+
+ if (textFormatFlags.HasFlag(TextFormatFlags.PreserveGraphicsTranslateTransform))
+ {
+ apply |= ApplyGraphicsProperties.TranslateTransform;
+ }
+
+ return apply;
+ }
}
}
diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TextRendererTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TextRendererTests.cs
index e4c0d23fc5d..8710362cfc5 100644
--- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TextRendererTests.cs
+++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TextRendererTests.cs
@@ -4,7 +4,10 @@
using System.Collections.Generic;
using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
using System.Drawing.Text;
+using System.IO;
using System.Windows.Forms.Metafiles;
using Moq;
using WinForms.Common.Tests;
@@ -747,5 +750,87 @@ public static TheoryData TextRenderer_MeasureText_Padding
{ TextFormatFlags.LeftAndRightPadding, new Size(82, 13) },
{ TextFormatFlags.NoPadding, new Size(71, 13) }
};
+
+ [WinFormsTheory]
+ [MemberData(nameof(TextRenderer_DrawText_ApplyState_TestData))]
+ public void TextRenderer_DrawText_ApplyState(TextFormatFlags flags, Rectangle expectedBounds)
+ {
+ using var hdc = new Interop.Gdi32.CreateDcScope(default);
+ DeviceContextState state = new DeviceContextState(hdc);
+
+ using MemoryStream stream = new MemoryStream(1024);
+ using (Metafile metafileRecorder = new Metafile(stream, hdc.HDC.Handle, EmfType.EmfOnly))
+ using (Graphics graphics = Graphics.FromImage(metafileRecorder))
+ {
+ using Matrix matrix = new Matrix();
+ matrix.Translate(5, 10);
+ graphics.Transform = matrix;
+ using Region region = new(new Rectangle(1, 2, 6, 8));
+ graphics.Clip = region;
+
+ TextRenderer.DrawText(
+ graphics,
+ "Landshark",
+ SystemFonts.DefaultFont,
+ new Rectangle(0, 0, int.MaxValue, int.MaxValue),
+ Color.Red,
+ flags);
+ }
+
+ // Need to queue the stream back to the beginning for the reader
+ stream.Position = 0;
+ using Metafile metafile = new Metafile(stream);
+ using var emf = new EmfScope((Interop.Gdi32.HENHMETAFILE)metafile.GetHenhmetafile());
+
+ emf.Validate(
+ state,
+ Validate.TextOut(
+ "Landshark",
+ expectedBounds,
+ State.FontFace(SystemFonts.DefaultFont.Name),
+ State.TextColor(Color.Red)));
+ }
+
+ public static TheoryData TextRenderer_DrawText_ApplyState_TestData
+ => new TheoryData
+ {
+ { TextFormatFlags.Default, new Rectangle(3, 0, 49, 12) },
+ { TextFormatFlags.PreserveGraphicsTranslateTransform, new Rectangle(8, 10, 49, 12) },
+ { TextFormatFlags.PreserveGraphicsClipping, new Rectangle(6, 12, 5, 0) },
+ { TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.PreserveGraphicsTranslateTransform, new Rectangle(8, 12, 3, 7) },
+ };
+
+ [WinFormsTheory]
+ [MemberData(nameof(TextRenderer_MeasureText_ApplyState_TestData))]
+ public void TextRenderer_MeasureText_ApplyState(TextFormatFlags flags, Size expectedSize)
+ {
+ using var image = new Bitmap(200, 50);
+ using Graphics graphics = Graphics.FromImage(image);
+ using Matrix matrix = new Matrix();
+ matrix.Translate(5, 10);
+ graphics.Transform = matrix;
+ using Region region = new(new Rectangle(1, 2, 6, 8));
+ graphics.Clip = region;
+
+ Size size = TextRenderer.MeasureText(
+ graphics,
+ "Landshark",
+ SystemFonts.DefaultFont,
+ new Size(int.MaxValue, int.MaxValue),
+ flags);
+
+ Assert.Equal(expectedSize, size);
+ }
+
+ public static TheoryData TextRenderer_MeasureText_ApplyState_TestData
+ => new TheoryData
+ {
+ // State application doesn't practially impact size measurements, but we still want to have a regession test
+ // here in case something sneaks in.
+ { TextFormatFlags.Default, new Size(57, 13) },
+ { TextFormatFlags.PreserveGraphicsTranslateTransform, new Size(57, 13) },
+ { TextFormatFlags.PreserveGraphicsClipping, new Size(57, 13) },
+ { TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.PreserveGraphicsTranslateTransform, new Size(57, 13) },
+ };
}
}