3

I would like to get the filename of a font. This can't be that hard... I am aware, there is a very similar question already, but the answer to that question just can't be it.

What I want to do is to send a Font file over TCP/IP to an other client, if he requests it. I select the desired font over a FontDialog, I can get the FontName from the framework. I can't find the font file in a way that I can say will work most of the time.

Where does .NET know which fonts are installed on the system? It can't be that the framework relies on a solution which does not work all the time, like the solution on CodeProject and suggested in Stackoverflow. There must be a secure way to retrieve the font file. The FontDialog can list them all in a box and the fonts installed must have a path to their file.

Anyone interested in helping me?

enter image description here

6
  • 1
    Possible duplicate of Get a font filename based on Font Name and Style (Bold/Italic) Commented Feb 3, 2014 at 10:55
  • 2
    This is a .NET Question with C# / Winforms technology, not C++.
    – El Mac
    Commented Feb 3, 2014 at 10:56
  • 1
    The proposed solution (enumerate HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts) is not related to the language. Commented Feb 3, 2014 at 10:58
  • 1
    As I understand it the framework does not offer the functionality that you need. So I guess that means that you have to give up. Deciding up front what the solution must be is rather silly. Present the problem, and let people propose the best solution. Commented Feb 3, 2014 at 11:16
  • 1
    Taking a dependency on the underlying font rendering engine implementation is something you'll deeply regret some day. Of course this is not something that's ever OS independent. There's a massive difference between TrueType and OpenType for example. A practical solution is to pick your font choices carefully and rely on the target machine at least supporting the "web safe" subset of fonts. Or to pass the buck to another chunk of code to solve this problem for you. PDF and XPS, being document formats that have a high stake at it, support font embedding for example. Commented Feb 3, 2014 at 12:25

3 Answers 3

10
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using Microsoft.Win32
public static string GetSystemFontFileName(Font font)
{
    RegistryKey fonts = null;
    try{
        fonts = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\Fonts", false);
        if(fonts == null)
        {
            fonts = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Fonts", false);
            if(fonts == null)
            {
                throw new Exception("Can't find font registry database.");
            }
        }
        
        string suffix = "";
        if(font.Bold)
            suffix += "(?: Bold)?";
        if(font.Italic)
            suffix += "(?: Italic)?";
        
        var regex = new Regex(@"^(?:.+ & )?"+Regex.Escape(font.Name)+@"(?: & .+)?(?<suffix>"+suffix+@") \(TrueType\)$");
        
        string[] names = fonts.GetValueNames();
        
        string name = names.Select(n => regex.Match(n)).Where(m => m.Success).OrderByDescending(m => m.Groups["suffix"].Length).Select(m => m.Value).FirstOrDefault();
        
        if(name != null)
        {
            return fonts.GetValue(name).ToString();
        }else{
            return null;
        }
    }finally{
        if(fonts != null)
        {
            fonts.Dispose();
        }
    }
}
3
  • Thanks IllidanS4 so much. Actually it doesn't worry about FontStyle. For example, if I want to get full path of Arial Bold, it doesn't work.
    – onurbaysan
    Commented Jul 22, 2015 at 13:41
  • Great answer, but doesn't quite cover all situations I've come across. For example, "Cambria (TrueType)" and "Cambria Math (TrueType)" aren't working on my computer because they appear in the registry as "Cambria & Cambria Math (TrueType)"
    – Jibb Smart
    Commented Oct 19, 2017 at 9:08
  • 1
    @JibbSmart Thanks for the observation! I have updated the answer to account for "compound fonts" or what the Cambria font is. I hope it will help.
    – IS4
    Commented Oct 19, 2017 at 19:03
2

For one, your problem describes issues with Windows OS. Hence your solution needs to be a Windows specific solution. In your comment you mentioned that the solution may not work on other OS.

It surely WILL NOT work.

Each OS will needs to be handled separately. Also, you can't assume that installation of fonts will happen in the same way on client's OS.

As for the problem with getting font file names. There is nothing wrong with the solutions provided on CP. In many instances the only way to get something in windows is to make API calls. .Net simply has no support for a number of things we may need to do. So relying on API is doesn't make it automatically wrong or undesirable.

EDIT:

In .NET 4.0 Fonts is a special folder that can be accessed like so

var fontsFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
8
  • Relying on APIs doesn't make it wrong. I can't remember have said that. As I am using .NET Framework, it surely IS a WINDOWS ONLY solution. I am not using Mono Project or any other port. The problem is that the solution provided by CodeProject may not even work on a windows environment.
    – El Mac
    Commented Feb 3, 2014 at 11:37
  • Since mono is out of the question in that case I don't see what the problem is. What do you mean doesn't work all the time? Could it be due to restricted access to registry?
    – Alex
    Commented Feb 3, 2014 at 11:40
  • I don't know if the registry entry (path) will change if I use Windows XP / Vista / 7 / 8. If there is no other way, I will have to work with the registry, even if it's a solution I don't like.
    – El Mac
    Commented Feb 3, 2014 at 11:42
  • A quick googler revealed that HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts is used on server but without NT on desktop. Perhaps not entirely relevant but certainly reinforces your worry. I guess you could have a switch statement that uses an appropriate path depending on environment. I'm starting to see your point btw :D
    – Alex
    Commented Feb 3, 2014 at 11:48
  • 1
    Btw in 4.0 Fonts is a special folder. What version are you using? msdn.microsoft.com/en-us/library/…
    – Alex
    Commented Feb 3, 2014 at 11:54
2
    Dictionary<string, List<string>> _fontNameToFiles; 
    /// <summary>
    /// This is a brute force way of finding the files that represent a particular
    /// font family.
    /// The first call may be quite slow.
    /// Only finds font files that are installed in the standard directory.
    /// Will not discover font files installed after the first call.
    /// </summary>
    /// <returns>enumeration of file paths (possibly none) that contain data
    /// for the specified font name</returns>
    private IEnumerable<string> GetFilesForFont(string fontName)
    {
        if (_fontNameToFiles == null)
        {
            _fontNameToFiles = new Dictionary<string, List<string>>();
            foreach (var fontFile in Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.Fonts)))
            {
                var fc = new PrivateFontCollection();
                try
                {
                    fc.AddFontFile(fontFile);
                }
                catch (FileNotFoundException)
                {
                    continue; // not sure how this can happen but I've seen it.
                }
                var name = fc.Families[0].Name;
                // If you care about bold, italic, etc, you can filter here.
                List<string> files;
                if (!_fontNameToFiles.TryGetValue(name, out files))
                {
                    files = new List<string>();
                    _fontNameToFiles[name] = files;
                }
                files.Add(fontFile);
            }
        }
        List<string> result;
        if (!_fontNameToFiles.TryGetValue(fontName,  out result))
            return new string[0];
        return result;
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.