Home > Back-end >  Getting the hDC for a System.Windows.Media.FontFamily
Getting the hDC for a System.Windows.Media.FontFamily

Time:11-17

I'm filtering fonts using the Panose.FontFamily Type, which I get using:

[System.Runtime.InteropServices.DllImport("gdi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
private static extern int GetOutlineTextMetricsA(IntPtr hdc, int cbData, IntPtr lpOtm);

However, I'm starting with a System.Windows.Media.FontFamily. Coming from the list: System.Windows.Media.Fonts.SystemFontFamilies

I haven't found a way to get the hDC for a Media.FontFamily. Therefore, I need to match it to System.Drawing.FontFamily. Coming from the list: System.Drawing.FontFamily.Families

The only way I've found to map a Media.FontFamily to a Drawing.FontFamily is by comparing Media.FontFamily.Source to Drawing.FontFamily.Name. Which is not perfect since their respective names often don't exactly match. I figure I'm getting a match for at least 90% of the fonts.

Using a Drawing.FontFamily does have a way to get the hDC of a font.

A decent solution will be a better way (other than by name) to map a Media.FontFamily to Drawing.FontFamily. An ideal solution would to get an hDC directly from a Media.FontFamily. Another ideal solution would be to get the OUTLINETEXTMETRICS struct without needing to import the base method. A perfect solution would be getting the panose family type from a Media.FontFamily with involving a Drawing.FontFamily or importing a method.

First, I get the list of Media.FontFamily like this:

foreach (Media.FontFamily font in Media.Fonts.SystemFontFamilies.OrderBy(_ => _.Source))

Then I get the list of Drawing.FontFamily like this:

System.Collections.IEnumerator fontIter = Draw.FontFamily.Families.OrderBy(_ => _.Name).GetEnumerator();

Then I compare the Media.FontFamily.Source to the Drawing.FontFamily.Name. I've found that Drawing.FontFamily.Name contains Media.FontFamily.Source quite often. After getting the Drawing.FontFamily, it's an easy step to convert it to a Drawing.Font:

Draw.Font drawFont = new Draw.Font(drawFamily, 9.0F);

And from that I get the Panose Family Type:

enum PanoseFontFamilyTypes
    {
        PAN_ANY = 0, PAN_NO_FIT = 1, PAN_FAMILY_TEXT_DISPLAY = 2, PAN_FAMILY_SCRIPT = 3,
        PAN_FAMILY_DECORATIVE = 4, PAN_FAMILY_PICTORIAL = 5
    }

public PanoseFontFamilyTypes PanoseFontFamilyType(Draw.Font font)
        {
            byte bFamilyType = 0;
            IntPtr hdc = (IntPtr)0;
            IntPtr hFontOld;

            try
            {
                hdc = GraphTool.GetHdc();
                hFontOld = SelectObject(hdc, font.ToHfont());
                int bufSize = GetOutlineTextMetricsA(hdc, 0, (IntPtr)0);
                IntPtr lpOtm = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(bufSize);
                System.Runtime.InteropServices.Marshal.WriteInt32(lpOtm, bufSize);
                int success = GetOutlineTextMetricsA(hdc, bufSize, lpOtm);
                if (success != 0)
                {
                    int offset = 61;
                    bFamilyType = System.Runtime.InteropServices.Marshal.ReadByte(lpOtm, offset);
                }

                System.Runtime.InteropServices.Marshal.FreeCoTaskMem(lpOtm);

                SelectObject(hdc, hFontOld);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Message: {ex.Message}\r\nStackTrace:\r\n{ex.StackTrace}", "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                GraphTool.ReleaseHdc(hdc);
            }

            return (PanoseFontFamilyTypes)bFamilyType;
        }

Looking for a less convoluted solution.

CodePudding user response:

I never did find a way to map a Media.FontFamily to a Drawing.FontFamily that did not involve a string comparison. However, I did find a more reliable way to do the mapping. The problem is that Media.FontFamily.Source (the font name) often does not exactly correspond to a Drawing.FontFamily.Name.

  Media.FontFamily font=<your font>;
  Typeface face = new Typeface(font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
  if (face.TryGetGlyphTypeface(out GlyphTypeface typeFace))
  {
    Drawing.FontFamily drawFont = Drawing.FontFamily.Families.FirstOrDefault(_o => typeFace.Win32FamilyNames.Any(_i => _i.Value.Equals(_o.Name, StringComparison.OrdinalIgnoreCase)));
    return new Tuple<FontUtility.OutlineTextMetric, bool>(GetOutlineTextMetric(drawFont, fontData), typeFace.Symbol);
  }

The GetOutlineTextMetric method Gets the OUTLINETEXTMETRIC using the code found here: http://pinvoke.net/default.aspx/gdi32/GetOutlineTextMetrics.html

The name in Win32FamilyNames does map exactly to a Drawing.FontFamily.Name. I was able to get rid of the contains string algorithm I was using.

This also has the added benefit of getting the Symbol boolean, which at least for every font I have installed is 100% accurate to identify a symbol font. (e.g. wingdings)

  • Related