/// <summary> /// <para> /// Returns the Size of the given text using the specified font if not null, otherwise the font currently /// set in the dc is used. /// This method is used to get the size in points of a line of text; it uses GetTextExtentPoint32 function /// which computes the width and height of the text ignoring TAB\CR\LF characters. /// A text extent is the distance between the beginning of the space and a character that will fit in the space. /// </para> /// </summary> public Size GetTextExtent(string text, WindowsFont font) { if (string.IsNullOrEmpty(text)) { return(Size.Empty); } IntNativeMethods.SIZE size = new IntNativeMethods.SIZE(); HandleRef hdc = new HandleRef(null, dc.Hdc); if (font != null) { dc.SelectFont(font); } IntUnsafeNativeMethods.GetTextExtentPoint32(hdc, text, size); // Unselect, but not from Measurement DC as it keeps the same // font selected for perf reasons. if (font != null && !MeasurementDCInfo.IsMeasurementDC(dc)) { dc.ResetFont(); } return(new Size(size.cx, size.cy)); }
/// <summary> /// Restores the device context to the specified state. The DC is restored by popping state information off a /// stack created by earlier calls to the SaveHdc function. /// The stack can contain the state information for several instances of the DC. If the state specified by the /// specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top /// of the stack and the specified instance. /// Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific /// instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative /// to the current state. For example, -1 restores the most recently saved state. /// See MSDN for more info. /// </summary> public void RestoreHdc() { #if TRACK_HDC bool result = #endif // Note: Don't use the Hdc property here, it would force handle creation. Interop.Gdi32.RestoreDC(new HandleRef(this, _hDC), -1); #if TRACK_HDC // Note: Winforms may call this method during app exit at which point the DC may have been finalized already causing this assert to popup. Debug.WriteLine(DbgUtil.StackTraceToStr(string.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8}, state={2})", result, unchecked ((int)_hDC), restoreState))); #endif Debug.Assert(_contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()"); if (_contextStack != null) { GraphicsState g = (GraphicsState)_contextStack.Pop() !; _hCurrentBmp = g.hBitmap; _hCurrentBrush = g.hBrush; _hCurrentPen = g.hPen; _hCurrentFont = g.hFont; } #if OPTIMIZED_MEASUREMENTDC // in this case, GDI will copy back the previously saved font into the DC. // we dont actually know what the font is in our measurement DC so // we need to clear it off. MeasurementDCInfo.ResetIfIsMeasurementDC(_hDC); #endif }
public void ResetFont() { #if OPTIMIZED_MEASUREMENTDC // in this case, GDI will copy back the previously saved font into the DC. // we dont actually know what the font is in our measurement DC so // we need to clear it off. MeasurementDCInfo.ResetIfIsMeasurementDC(this.Hdc); #endif IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef(null, hInitialFont)); selectedFont = null; hCurrentFont = hInitialFont; }
/// <devdoc> /// Selects the specified object into the dc. If the specified object is the same as the one currently selected /// in the dc, the object is not set and a null value is returned. /// </devdoc> public IntPtr SelectFont(WindowsFont font) { // Fonts are one of the most expensive objects to select in an hdc and in many cases we are passed a Font that is the // same as the one already selected in the dc so to avoid a perf hit we get the hdc font's log font and compare it // with the one passed in before selecting it in the hdc. // Also, we avoid performing GDI operations that if done on an enhanced metafile DC would add an entry to it, hence // reducing the size of the metafile. if (font.Equals(this.Font)) { return(IntPtr.Zero); } IntPtr result = SelectObject(font.Hfont, GdiObjectType.Font); WindowsFont previousFont = selectedFont; selectedFont = font; hCurrentFont = font.Hfont; // the measurement DC always leaves fonts selected for pref reasons. // in this case, we need to diposse the font since the original // creator didn't fully dispose. if (previousFont != null) { #if DEBUG //Disabling this assert until DevDiv Bugs 95968 gets fixed //Debug.Assert(previousFont.Hfont != IntPtr.Zero, "A font has been disposed before it was unselected from the DC. This will leak a GDI handle on Win9x! Call ResetFont()"); #endif if (MeasurementDCInfo.IsMeasurementDC(this)) { previousFont.Dispose(); } } #if OPTIMIZED_MEASUREMENTDC // once we've changed the font, update the last used font. if (MeasurementDCInfo.IsMeasurementDC(this)) { if (result != IntPtr.Zero) { MeasurementDCInfo.LastUsedFont = font; } else { // there was an error selecting the Font into the DC, we dont know anything about it. MeasurementDCInfo.Reset(); } } #endif return(result); }
/// <devdoc> /// Restores the device context to the specified state. The DC is restored by popping state information off a /// stack created by earlier calls to the SaveHdc function. /// The stack can contain the state information for several instances of the DC. If the state specified by the /// specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top /// of the stack and the specified instance. /// Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific /// instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative /// to the current state. For example, -1 restores the most recently saved state. /// See MSDN for more info. /// </devdoc> public void RestoreHdc() { #if TRACK_HDC bool result = #endif // Note: Don't use the Hdc property here, it would force handle creation. IntUnsafeNativeMethods.RestoreDC(new HandleRef(this, this.hDC), -1); #if TRACK_HDC // Note: Winforms may call this method during app exit at which point the DC may have been finalized already causing this assert to popup. Debug.WriteLine(DbgUtil.StackTraceToStr(String.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8})", result, unchecked ((int)this.hDC)))); #endif Debug.Assert(contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()"); if (contextStack != null) { GraphicsState g = (GraphicsState)contextStack.Pop(); hCurrentBmp = g.hBitmap; hCurrentBrush = g.hBrush; hCurrentPen = g.hPen; hCurrentFont = g.hFont; #if !DRAWING_NAMESPACE if (g.font != null && g.font.IsAlive) { selectedFont = g.font.Target as WindowsFont; } else { WindowsFont previousFont = selectedFont; selectedFont = null; if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this)) { previousFont.Dispose(); } } #endif } #if OPTIMIZED_MEASUREMENTDC // in this case, GDI will copy back the previously saved font into the DC. // we dont actually know what the font is in our measurement DC so // we need to clear it off. MeasurementDCInfo.ResetIfIsMeasurementDC(this.hDC); #endif }
/// <devdoc> /// 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: /// - If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by /// the lpRect parameter and extends the base of the rectangle to bound the last line of text. /// - If the largest word is wider than the rectangle, the width is expanded. /// - If the text is less than the width of the rectangle, the width is reduced. /// - If there is only one line of text, DrawText modifies the right side of the rectangle so that /// it bounds the last character in the line. /// If the font is null, the hdc's current font will be used. /// /// Note for vertical fonts (if ever supported): DrawTextEx uses GetTextExtentPoint32 for measuring the text and this /// function has the following limitation (from MSDN): /// - This function assumes that the text is horizontal, that is, that the escapement is always 0. This is true for both /// the horizontal and vertical measurements of the text. The application must convert it explicitly. /// </devdoc> public Size MeasureText(string text, WindowsFont font, Size proposedSize, IntTextFormatFlags flags) { Debug.Assert(((uint)flags & GdiUnsupportedFlagMask) == 0, "Some custom flags were left over and are not GDI compliant!"); if (string.IsNullOrEmpty(text)) { return(Size.Empty); } // // DrawText returns a rectangle useful for aligning, but not guaranteed to encompass all // pixels (its not a FitBlackBox, if the text is italicized, it will overhang on the right.) // So we need to account for this. // IntNativeMethods.DRAWTEXTPARAMS dtparams = null; #if OPTIMIZED_MEASUREMENTDC // use the cache if we've got it if (MeasurementDCInfo.IsMeasurementDC(this.DeviceContext)) { dtparams = MeasurementDCInfo.GetTextMargins(this, font); } #endif if (dtparams == null) { dtparams = GetTextMargins(font); } // // If Width / Height are < 0, we need to make them larger or DrawText will return // an unbounded measurement when we actually trying to make it very narrow. // int minWidth = 1 + dtparams.iLeftMargin + dtparams.iRightMargin; if (proposedSize.Width <= minWidth) { proposedSize.Width = minWidth; } if (proposedSize.Height <= 0) { proposedSize.Height = 1; } IntNativeMethods.RECT rect = IntNativeMethods.RECT.FromXYWH(0, 0, proposedSize.Width, proposedSize.Height); HandleRef hdc = new HandleRef(null, this.dc.Hdc); if (font != null) { this.dc.SelectFont(font); } // If proposedSize.Height >= MaxSize.Height it is assumed bounds needed. If flags contain SingleLine and // VerticalCenter or Bottom options, DrawTextEx does not bind the rectangle to the actual text height since // it assumes the text is to be vertically aligned; we need to clear the VerticalCenter and Bottom flags to // get the actual text bounds. if (proposedSize.Height >= MaxSize.Height && (flags & IntTextFormatFlags.SingleLine) != 0) { // Clear vertical-alignment flags. flags &= ~(IntTextFormatFlags.Bottom | IntTextFormatFlags.VerticalCenter); } if (proposedSize.Width == MaxSize.Width) { // PERF: No constraining width means no word break. // in this case, we dont care about word wrapping - there should be enough room to fit it all flags &= ~(IntTextFormatFlags.WordBreak); } flags |= IntTextFormatFlags.CalculateRectangle; IntUnsafeNativeMethods.DrawTextEx(hdc, text, ref rect, (int)flags, dtparams); /* No need to restore previous objects into the dc (see comments on top of the class). * * if( hOldFont != IntPtr.Zero ) * { * this.dc.SelectObject(hOldFont); * } */ return(rect.Size); }