| File: System\Drawing\Printing\PageSettings.cs | Web Access |
| Project: src\winforms\src\System.Drawing.Common\src\System.Drawing.Common.csproj (System.Drawing.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Drawing.Printing; /// <summary> /// Specifies settings that apply to a single page. /// </summary> public unsafe class PageSettings : ICloneable { private PrinterSettings _printerSettings; private TriState _color = TriState.Default; private PaperSize? _paperSize; private PaperSource? _paperSource; private PrinterResolution? _printerResolution; private TriState _landscape = TriState.Default; private Margins _margins = new(); /// <summary> /// Initializes a new instance of the <see cref='PageSettings'/> class using the default printer. /// </summary> public PageSettings() : this(new PrinterSettings()) { } /// <summary> /// Initializes a new instance of the <see cref='PageSettings'/> class using the specified printer. /// </summary> public PageSettings(PrinterSettings printerSettings) { Debug.Assert(printerSettings is not null, "printerSettings == null"); _printerSettings = printerSettings; } /// <summary> /// Gets the bounds of the page, taking into account the Landscape property. /// </summary> public Rectangle Bounds { get { HGLOBAL modeHandle = (HGLOBAL)_printerSettings.GetHdevmode(); Rectangle pageBounds = GetBounds(modeHandle); PInvokeCore.GlobalFree(modeHandle); return pageBounds; } } /// <summary> /// Gets or sets a value indicating whether the page is printed in color. /// </summary> public bool Color { get => _color.IsDefault ? _printerSettings.GetModeField(ModeField.Color, (short)DEVMODE_COLOR.DMCOLOR_MONOCHROME) == (short)DEVMODE_COLOR.DMCOLOR_COLOR : (bool)_color; set => _color = value; } /// <summary> /// Returns the x dimension of the hard margin /// </summary> public float HardMarginX { get { using var hdc = _printerSettings.CreateDeviceContext(this); int dpiX = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSX); int hardMarginX_DU = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETX); return hardMarginX_DU * 100 / dpiX; } } /// <summary> /// Returns the y dimension of the hard margin. /// </summary> public float HardMarginY { get { using var hdc = _printerSettings.CreateDeviceContext(this); int dpiY = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSY); int hardMarginY_DU = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETY); return hardMarginY_DU * 100 / dpiY; } } /// <summary> /// Gets or sets a value indicating whether the page should be printed in landscape or portrait orientation. /// </summary> public bool Landscape { get => _landscape.IsDefault ? _printerSettings.GetModeField(ModeField.Orientation, (short)PInvokeCore.DMORIENT_PORTRAIT) == PInvokeCore.DMORIENT_LANDSCAPE : (bool)_landscape; set => _landscape = value; } /// <summary> /// Gets or sets a value indicating the margins for this page. /// </summary> public Margins Margins { get => _margins; set => _margins = value; } /// <summary> /// Gets or sets the paper size. /// </summary> public PaperSize PaperSize { get => GetPaperSize(HGLOBAL.Null); set => _paperSize = value; } /// <summary> /// Gets or sets a value indicating the paper source (i.e. upper bin). /// </summary> public PaperSource PaperSource { get { if (_paperSource is not null) { return _paperSource; } HGLOBAL modeHandle = (HGLOBAL)_printerSettings.GetHdevmode(); DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock(modeHandle); PaperSource result = PaperSourceFromMode(devmode); PInvokeCore.GlobalUnlock(modeHandle); PInvokeCore.GlobalFree(modeHandle); return result; } set => _paperSource = value; } /// <summary> /// Gets the PrintableArea for the printer. Units = 100ths of an inch. /// </summary> public RectangleF PrintableArea { get { RectangleF printableArea = default; using var hdc = _printerSettings.CreateInformationContext(this); int dpiX = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSX); int dpiY = PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.LOGPIXELSY); if (!Landscape) { // Need to convert the printable area to 100th of an inch from the device units printableArea.X = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETX) * 100 / dpiX; printableArea.Y = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETY) * 100 / dpiY; printableArea.Width = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES) * 100 / dpiX; printableArea.Height = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES) * 100 / dpiY; } else { // Need to convert the printable area to 100th of an inch from the device units printableArea.Y = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETX) * 100 / dpiX; printableArea.X = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.PHYSICALOFFSETY) * 100 / dpiY; printableArea.Height = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.HORZRES) * 100 / dpiX; printableArea.Width = (float)PInvokeCore.GetDeviceCaps(hdc, GET_DEVICE_CAPS_INDEX.VERTRES) * 100 / dpiY; } return printableArea; } } /// <summary> /// Gets or sets the printer resolution for the page. /// </summary> public PrinterResolution PrinterResolution { get { if (_printerResolution is not null) { return _printerResolution; } HGLOBAL modeHandle = (HGLOBAL)_printerSettings.GetHdevmode(); DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock(modeHandle); PrinterResolution result = PrinterResolutionFromMode(devmode); PInvokeCore.GlobalUnlock(modeHandle); PInvokeCore.GlobalFree(modeHandle); return result; } set => _printerResolution = value; } /// <summary> /// Gets or sets the associated printer settings. /// </summary> public PrinterSettings PrinterSettings { get => _printerSettings; set => _printerSettings = value ?? new PrinterSettings(); } /// <summary> /// Copies the settings and margins. /// </summary> public object Clone() { PageSettings result = (PageSettings)MemberwiseClone(); result._margins = (Margins)_margins.Clone(); return result; } /// <summary> /// Copies the relevant information out of the PageSettings and into the handle. /// </summary> public void CopyToHdevmode(IntPtr hdevmode) { if (hdevmode == 0) { throw new ArgumentNullException(nameof(hdevmode)); } DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock((HGLOBAL)hdevmode); if (_color.IsNotDefault && devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_COLOR)) { devmode->dmColor = _color.IsTrue ? DEVMODE_COLOR.DMCOLOR_COLOR : DEVMODE_COLOR.DMCOLOR_MONOCHROME; } if (_landscape.IsNotDefault && devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_ORIENTATION)) { devmode->dmOrientation = _landscape.IsTrue ? (short)PInvokeCore.DMORIENT_LANDSCAPE : (short)PInvokeCore.DMORIENT_PORTRAIT; } if (_paperSize is not null) { if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PAPERSIZE)) { devmode->dmPaperSize = (short)_paperSize.RawKind; } bool setWidth = false; bool setLength = false; if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PAPERLENGTH)) { // dmPaperLength is always in tenths of millimeter but paperSizes are in hundredth of inch .. // so we need to convert :: use PrinterUnitConvert.Convert(value, PrinterUnit. // TenthsOfAMillimeter /*fromUnit*/, PrinterUnit.Display /*ToUnit*/) int length = PrinterUnitConvert.Convert(_paperSize.Height, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); devmode->dmPaperLength = (short)length; setLength = true; } if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PAPERWIDTH)) { int width = PrinterUnitConvert.Convert(_paperSize.Width, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); devmode->dmPaperWidth = (short)width; setWidth = true; } if (_paperSize.Kind == PaperKind.Custom) { if (!setLength) { devmode->dmFields |= DEVMODE_FIELD_FLAGS.DM_PAPERLENGTH; int length = PrinterUnitConvert.Convert(_paperSize.Height, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); devmode->dmPaperLength = (short)length; } if (!setWidth) { devmode->dmFields |= DEVMODE_FIELD_FLAGS.DM_PAPERWIDTH; int width = PrinterUnitConvert.Convert(_paperSize.Width, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); devmode->dmPaperWidth = (short)width; } } } if (_paperSource is not null && devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_DEFAULTSOURCE)) { devmode->dmDefaultSource = (short)_paperSource.RawKind; } if (_printerResolution is not null) { if (_printerResolution.Kind == PrinterResolutionKind.Custom) { if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PRINTQUALITY)) { devmode->dmPrintQuality = (short)_printerResolution.X; } if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_YRESOLUTION)) { devmode->dmYResolution = (short)_printerResolution.Y; } } else { if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PRINTQUALITY)) { devmode->dmPrintQuality = (short)_printerResolution.Kind; } } } // It's possible this page has a DEVMODE for a different printer than the DEVMODE passed in here // (Ex: occurs when Doc.DefaultPageSettings.PrinterSettings.PrinterName != Doc.PrinterSettings.PrinterName) // // If the passed in devmode has fewer bytes than our buffer for the extra info, we want to skip the merge // as it will cause a buffer overrun. if (devmode->dmDriverExtra >= ExtraBytes) { fixed (char* n = _printerSettings.PrinterName) { int result = PInvoke.DocumentProperties( HWND.Null, global::Windows.Win32.Graphics.Printing.PRINTER_HANDLE.Null, n, devmode, devmode, (uint)(DEVMODE_FIELD_FLAGS.DM_IN_BUFFER | DEVMODE_FIELD_FLAGS.DM_OUT_BUFFER)); if (result < 0) { PInvokeCore.GlobalFree((HGLOBAL)hdevmode); } } } PInvokeCore.GlobalUnlock((HGLOBAL)hdevmode); } private short ExtraBytes { get { HGLOBAL modeHandle = _printerSettings.GetHdevmodeInternal(); DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock(modeHandle); short result = devmode is null ? default : (short)devmode->dmDriverExtra; PInvokeCore.GlobalUnlock(modeHandle); PInvokeCore.GlobalFree(modeHandle); return result; } } internal Rectangle GetBounds(HGLOBAL modeHandle) { PaperSize size = GetPaperSize(modeHandle); return IsLandscape(modeHandle) ? new Rectangle(0, 0, size.Height, size.Width) : new Rectangle(0, 0, size.Width, size.Height); bool IsLandscape(HGLOBAL modeHandle) => _landscape.IsDefault ? _printerSettings.GetModeField(ModeField.Orientation, (short)PInvokeCore.DMORIENT_PORTRAIT, modeHandle) == PInvokeCore.DMORIENT_LANDSCAPE : (bool)_landscape; } private PaperSize GetPaperSize(HGLOBAL modeHandle) { if (_paperSize is not null) { return _paperSize; } bool ownHandle = false; if (modeHandle.IsNull) { modeHandle = (HGLOBAL)_printerSettings.GetHdevmode(); ownHandle = true; } DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock(modeHandle); PaperSize result = PaperSizeFromMode(devmode); PInvokeCore.GlobalUnlock(modeHandle); if (ownHandle) { PInvokeCore.GlobalFree(modeHandle); } return result; } private PaperSize PaperSizeFromMode(DEVMODEW* devmode) { PaperSize[] sizes = _printerSettings.Get_PaperSizes(); if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PAPERSIZE)) { for (int i = 0; i < sizes.Length; i++) { if (sizes[i].RawKind == devmode->Anonymous1.Anonymous1.dmPaperSize) { return sizes[i]; } } } return new PaperSize( PaperKind.Custom, "custom", PrinterUnitConvert.Convert(devmode->Anonymous1.Anonymous1.dmPaperWidth, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display), PrinterUnitConvert.Convert(devmode->Anonymous1.Anonymous1.dmPaperLength, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display)); } private PaperSource PaperSourceFromMode(DEVMODEW* devmode) { PaperSource[] sources = _printerSettings.Get_PaperSources(); if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_DEFAULTSOURCE)) { for (int i = 0; i < sources.Length; i++) { // The dmDefaultSource == to the RawKind in the PaperSource and not the Kind // if the PaperSource is populated with CUSTOM values. if ((short)sources[i].RawKind == devmode->dmDefaultSource) { return sources[i]; } } } return new PaperSource((PaperSourceKind)devmode->dmDefaultSource, "unknown"); } private PrinterResolution PrinterResolutionFromMode(DEVMODEW* devmode) { PrinterResolution[] resolutions = _printerSettings.Get_PrinterResolutions(); for (int i = 0; i < resolutions.Length; i++) { if (devmode->dmPrintQuality >= 0 && devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PRINTQUALITY) && devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_YRESOLUTION)) { if (resolutions[i].X == devmode->dmPrintQuality && resolutions[i].Y == devmode->dmYResolution) { return resolutions[i]; } } else { if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_PRINTQUALITY) && resolutions[i].Kind == (PrinterResolutionKind)devmode->dmPrintQuality) { return resolutions[i]; } } } return new PrinterResolution( PrinterResolutionKind.Custom, devmode->dmPrintQuality, devmode->dmYResolution); } /// <summary> /// Copies the relevant information out of the handle and into the PageSettings. /// </summary> public void SetHdevmode(IntPtr hdevmode) { if (hdevmode == 0) { throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevmode)); } DEVMODEW* devmode = (DEVMODEW*)PInvokeCore.GlobalLock((HGLOBAL)hdevmode); if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_COLOR)) { _color = devmode->dmColor == DEVMODE_COLOR.DMCOLOR_COLOR; } if (devmode->dmFields.HasFlag(DEVMODE_FIELD_FLAGS.DM_ORIENTATION)) { _landscape = devmode->dmOrientation == PInvokeCore.DMORIENT_LANDSCAPE; } _paperSize = PaperSizeFromMode(devmode); _paperSource = PaperSourceFromMode(devmode); _printerResolution = PrinterResolutionFromMode(devmode); PInvokeCore.GlobalUnlock((HGLOBAL)hdevmode); } public override string ToString() => $"[{nameof(PageSettings)}: Color={Color}, Landscape={Landscape}, Margins={Margins}, PaperSize={PaperSize}, PaperSource={PaperSource}, PrinterResolution={PrinterResolution}]"; }