示例#1
0
        /// <summary>
        ///  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.
        /// </summary>
        public Size GetTextExtent(string?text, WindowsFont?font)
        {
            if (string.IsNullOrEmpty(text))
            {
                return(Size.Empty);
            }

            Size size = new Size();

            if (font != null)
            {
                DeviceContext.SelectFont(font);
            }

            Gdi32.GetTextExtentPoint32W(DeviceContext.Hdc, text, text.Length, ref size);

            // Unselect, but not from Measurement DC as it keeps the same
            // font selected for perf reasons.
            if (font != null && !MeasurementDCInfo.IsMeasurementDC(DeviceContext))
            {
                DeviceContext.ResetFont();
            }

            return(new Size(size.Width, size.Height));
        }
示例#2
0
        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);

            IntPtr hdc = dc.GetHdc();

            try
            {
                using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
                using WindowsFont? wf    = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
                return(wg.MeasureText(text, wf, proposedSize));
            }
            finally
            {
                dc.ReleaseHdc();
            }
        }
示例#3
0
        /// <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()
        {
            // Note: Don't use the Hdc property here, it would force handle creation.
            Gdi32.RestoreDC(new HandleRef(this, _hDC), -1);
            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 (g.font != null && g.font.IsAlive)
                {
                    ActiveFont = g.font.Target as WindowsFont;
                }
                else
                {
                    WindowsFont?previousFont = ActiveFont;
                    ActiveFont = null;
                    if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this))
                    {
                        previousFont.Dispose();
                    }
                }
            }

            // 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);
        }
示例#4
0
        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, quality);
            wg.DrawText(text, wf, bounds, foreColor, backColor, flags);
        }
示例#5
0
 internal void UpdateFont(WindowsFont?font)
 {
     if (LastUsedFont != font)
     {
         LastUsedFont    = font;
         LeftTextMargin  = -1;
         RightTextMargin = -1;
     }
 }
示例#6
0
        public static Size MeasureText(string?text, Font?font, Size proposedSize, TextFormatFlags flags)
        {
            if (string.IsNullOrEmpty(text))
            {
                return(Size.Empty);
            }

            using WindowsFont? wf = WindowsGraphicsCacheManager.GetWindowsFont(font);
            return(WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf, proposedSize, GetTextFormatFlags(flags)));
        }
示例#7
0
        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));
        }
示例#8
0
        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));
        }
        internal static bool IsFontInUse(WindowsFont?wf)
        {
            if (wf == null || t_activeDeviceContexts == null)
            {
                return(false);
            }

            for (int i = 0; i < t_activeDeviceContexts.Count; i++)
            {
                if (t_activeDeviceContexts[i] is DeviceContext dc && (dc.ActiveFont == wf || dc.IsFontOnContextStack(wf)))
                {
                    return(true);
                }
            }

            return(false);
        }
示例#10
0
        /// <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.
            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})", 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 (g.font != null && g.font.IsAlive)
                {
                    ActiveFont = g.font.Target as WindowsFont;
                }
                else
                {
                    WindowsFont?previousFont = ActiveFont;
                    ActiveFont = null;
                    if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this))
                    {
                        previousFont.Dispose();
                    }
                }
            }

#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
        }
示例#11
0
        /// <summary>
        ///  Calculates the spacing required for drawing text w/o clipping parts of a glyph.
        /// </summary>
        public float GetOverhangPadding(WindowsFont?font)
        {
            // Some parts of a glyphs may be clipped depending on the font & font style, GDI+ adds 1/6 of tmHeight
            // to each size of the text bounding box when drawing text to account for that; we do it here as well.

            WindowsFont?tmpfont = font;

            if (tmpfont == null)
            {
                tmpfont = DeviceContext.Font;
            }

            float overhangPadding = tmpfont.Height / 6f;

            if (tmpfont != font)
            {
                tmpfont.Dispose();
            }

            return(overhangPadding);
        }
示例#12
0
        public static void DrawText(IDeviceContext dc, string?text, Font?font, Rectangle bounds, Color foreColor)
        {
            if (dc == null)
            {
                throw new ArgumentNullException(nameof(dc));
            }

            Gdi32.QUALITY fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics);

            IntPtr hdc = dc.GetHdc();

            try
            {
                using WindowsGraphics wg = WindowsGraphics.FromHdc(hdc);
                using WindowsFont? wf    = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality);
                wg.DrawText(text, wf, bounds, foreColor);
            }
            finally
            {
                dc.ReleaseHdc();
            }
        }
示例#13
0
        /// <summary>
        ///  Get the bounding box internal text padding to be used when drawing text.
        /// </summary>
        public User32.DRAWTEXTPARAMS GetTextMargins(WindowsFont?font)
        {
            // DrawText(Ex) adds a small space at the beginning of the text bounding box but not at the end,
            // this is more noticeable when the font has the italic style.  We compensate with this factor.

            int   leftMargin  = 0;
            int   rightMargin = 0;
            float overhangPadding;

            switch (TextPadding)
            {
            case TextPaddingOptions.GlyphOverhangPadding:
                // [overhang padding][Text][overhang padding][italic padding]
                overhangPadding = GetOverhangPadding(font);
                leftMargin      = (int)Math.Ceiling(overhangPadding);
                rightMargin     = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor));
                break;

            case TextPaddingOptions.LeftAndRightPadding:
                // [2 * overhang padding][Text][2 * overhang padding][italic padding]
                overhangPadding = GetOverhangPadding(font);
                leftMargin      = (int)Math.Ceiling(2 * overhangPadding);
                rightMargin     = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor));
                break;

            case TextPaddingOptions.NoPadding:
            default:
                break;
            }

            return(new User32.DRAWTEXTPARAMS
            {
                iLeftMargin = leftMargin,
                iRightMargin = rightMargin
            });
        }
示例#14
0
 /// <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);
示例#15
0
        /// <summary>
        ///  Draws the text in the given bounds, using the given Font, foreColor and backColor, and according to the specified
        ///  TextFormatFlags flags.
        ///
        ///  If font is null, the font currently selected in the hdc is used.
        ///
        ///  If foreColor and/or backColor are Color.Empty, the hdc current text and/or background color are used.
        /// </summary>
        public void DrawText(string?text, WindowsFont?font, Rectangle bounds, Color foreColor, Color backColor, User32.DT flags)
        {
            if (string.IsNullOrEmpty(text) || foreColor == Color.Transparent)
            {
                return;
            }

            Debug.Assert(((uint)flags & GdiUnsupportedFlagMask) == 0, "Some custom flags were left over and are not GDI compliant!");

            // DrawText requires default text alignment.
            if (DeviceContext.TextAlignment != default)
            {
                DeviceContext.TextAlignment = default;
            }

            // color empty means use the one currently selected in the dc.

            if (!foreColor.IsEmpty && foreColor != DeviceContext.TextColor)
            {
                DeviceContext.TextColor = foreColor;
            }

            if (font != null)
            {
                DeviceContext.SelectFont(font);
            }

            Gdi32.BKMODE newBackGndMode = (backColor.IsEmpty || backColor == Color.Transparent) ?
                                          Gdi32.BKMODE.TRANSPARENT :
                                          Gdi32.BKMODE.OPAQUE;

            if (DeviceContext.BackgroundMode != newBackGndMode)
            {
                DeviceContext.SetBackgroundMode(newBackGndMode);
            }

            if (newBackGndMode != Gdi32.BKMODE.TRANSPARENT && backColor != DeviceContext.BackgroundColor)
            {
                DeviceContext.BackgroundColor = backColor;
            }

            User32.DRAWTEXTPARAMS dtparams = GetTextMargins(font);

            bounds = AdjustForVerticalAlignment(DeviceContext, text, bounds, flags, ref dtparams);

            // Adjust unbounded rect to avoid overflow since Rectangle ctr does not do param validation.
            if (bounds.Width == MaxSize.Width)
            {
                bounds.Width -= bounds.X;
            }
            if (bounds.Height == MaxSize.Height)
            {
                bounds.Height -= bounds.Y;
            }

            var rect = new RECT(bounds);

            User32.DrawTextExW(DeviceContext, text, text.Length, ref rect, flags, ref dtparams);

            // No need to restore previous objects into the dc (see comments on top of the class).
        }
示例#16
0
 /// <summary>
 ///  Draws the text in the given bounds, using the given Font and foreColor, and according to the specified flags.
 /// </summary>
 public void DrawText(string?text, WindowsFont?font, Rectangle bounds, Color color, User32.DT flags)
 => DrawText(text, font, bounds, color, Color.Empty, flags);
示例#17
0
        /// <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:
        ///  - 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.
        /// </summary>
        public Size MeasureText(string?text, WindowsFont?font, Size proposedSize, User32.DT 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.

            User32.DRAWTEXTPARAMS dtparams;
            // use the cache if we've got it
            if (MeasurementDCInfo.IsMeasurementDC(DeviceContext))
            {
                dtparams = MeasurementDCInfo.GetTextMargins(this, font);
            }
            else
            {
                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;
            }

            var rect = new RECT(0, 0, proposedSize.Width, proposedSize.Height);

            if (font != null)
            {
                DeviceContext.SelectFont(font);
            }

            // If proposedSize.Height >= MaxSize.Height it is assumed bounds needed.  If flags contain SINGLELINE and
            // VCENTER 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 VCENTER and BOTTOM flags to
            // get the actual text bounds.
            if (proposedSize.Height >= MaxSize.Height && (flags & User32.DT.SINGLELINE) != 0)
            {
                // Clear vertical-alignment flags.
                flags &= ~(User32.DT.BOTTOM | User32.DT.VCENTER);
            }

            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 &= ~(User32.DT.WORDBREAK);
            }

            flags |= User32.DT.CALCRECT;
            User32.DrawTextExW(DeviceContext.Hdc, text, text.Length, ref rect, flags, ref dtparams);

            return(rect.Size);
        }
示例#18
0
 /// <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);
示例#19
0
 /// <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);
示例#20
0
 /// <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>
        ///  Get the cached WindowsFont associated with the specified font if one exists, otherwise create one and
        ///  add it to the cache.
        /// </summary>
        public static WindowsFont?GetWindowsFont(Font?font, Gdi32.QUALITY fontQuality = Gdi32.QUALITY.DEFAULT)
        {
            if (font == null)
            {
                return(null);
            }

            // First check if font is in the cache.

            int count = 0;
            int index = t_currentIndex;

            // Search by index of most recently added object.
            while (count < WindowsFontCache.Count)
            {
                if (WindowsFontCache[index].Key.Equals(font))  // don't do shallow comparison, we could miss cloned fonts.
                {
                    // We got a Font in the cache, let's see if we have a WindowsFont with the same quality as required by the caller.

                    // WARNING: It is not expected that the WindowsFont is disposed externally since it is created by this class.
                    Debug.Assert(WindowsFontCache[index].Value.Hfont != IntPtr.Zero, "Cached WindowsFont was disposed, enable GDI_FINALIZATION_WATCH to track who did it!");

                    WindowsFont wf = WindowsFontCache[index].Value;
                    if (wf.Quality == fontQuality)
                    {
                        return(wf);
                    }
                }

                index--;
                count++;

                if (index < 0)
                {
                    index = CacheSize - 1;
                }
            }

            // Font is not in the cache, let's add it.

            WindowsFont winFont = WindowsFont.FromFont(font, fontQuality);
            KeyValuePair <Font, WindowsFont> newEntry = new KeyValuePair <Font, WindowsFont>(font, winFont);

            t_currentIndex++;

            if (t_currentIndex == CacheSize)
            {
                t_currentIndex = 0;
            }

            if (WindowsFontCache.Count == CacheSize)  // No more room, update current index.
            {
                WindowsFont?wfont = null;

                // Go through the existing fonts in the cache, and see if any
                // are not in use by a DC.  If one isn't, replace that.  If
                // all are in use, new up a new font and do not cache it.

                bool finished   = false;
                int  startIndex = t_currentIndex;
                int  loopIndex  = startIndex + 1;
                while (!finished)
                {
                    if (loopIndex >= CacheSize)
                    {
                        loopIndex = 0;
                    }

                    if (loopIndex == startIndex)
                    {
                        finished = true;
                    }

                    wfont = WindowsFontCache[loopIndex].Value;
                    if (!DeviceContexts.IsFontInUse(wfont))
                    {
                        t_currentIndex = loopIndex;
                        finished       = true;
                        break;
                    }
                    else
                    {
                        loopIndex++;
                        wfont = null;
                    }
                }

                if (wfont != null)
                {
                    WindowsFontCache[t_currentIndex] = newEntry;
                    winFont.OwnedByCacheManager      = true;

                    wfont.OwnedByCacheManager = false;
                    wfont.Dispose();
                }
                else
                {
                    // do not cache font - caller is ALWAYS responsible for
                    // disposing now.  If it is owned  by the CM, it will not
                    // disposed.
                    winFont.OwnedByCacheManager = false;
                }
            }
            else
            {
                winFont.OwnedByCacheManager = true;
                WindowsFontCache.Add(newEntry);
            }
            return(winFont);
        }
示例#22
0
 /// <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);
示例#23
0
 /// <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);
示例#24
0
 /// <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);