问题
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?
回答1:
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\)$", RegexOptions.Compiled);
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();
}
}
}
回答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);
回答3:
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;
}
来源:https://stackoverflow.com/questions/21525377/retrieve-filename-of-a-font