Issue
How can I get a list of font names used in Revit and AutoCAD? In particular, I'm interested in getting a list of font names starting with "@", such as "@Arial Unicode MS" and "@Batang". They appear at the top of the list in those Autodesk products. They are used to draw characters in sideways, makeing it possible to write, for example, Japanese characters from top to bottom.) I tried System.Drawing.FontFamily.Families and System.Windows.Media.Fonts.SystemFontFamilies. But they don't list those fonts that start with "@".
Solution
Revit and AutoCAD uses gdi32 call "EnumFontFamiliesEx" which is available for C++ API. Unfortunately, there is no equivalent function in C#. A workaround is to use P/Invoke or platform invoke. There are quite a few examples to do this if you search the internet. For example, the following is what I found on the Visual Studio Developer Center:
Visual Studio Developer Center > Visual C# Forums > Visual C# General > How can I get all active fonts in C#? Having issues with gdi32 callback.
Another is at this discussion forum:
GNT A Generation of New Technology > Technology > Forum > Win32 Programmer > GDI > EnumFontFamiliesEx in C#
Following the above posts, I did tried it myself, and I was able to obtains the names of fonts including ones starting with "@".
Since I took most of the code from the above links, I will refer to them for the portion that I did not modify. The code below are to show you how I adapted and modified from the original code for the purpose of my experiment. I hope you get an idea.
class EnumFontFamiliesEx_CS
{
#region EnumFont
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern int EnumFontFamiliesEx(
IntPtr hdc,
[In] IntPtr pLogfont,
EnumFontExDelegate lpEnumFontFamExProc,
IntPtr lParam,
uint dwFlags);
// Copy the following from the example in the first link:
// class LOGFONT, enum FontWeight, FontCharSet,
// FontPrecision, FontClipPrecision, FontQuality,
// FontPitchAndFamily, struct NEWTEXTMETRIC, FONTSIGNATURE,
// NEWTEXTMETRICES, ENUMLOGFONTEX, const, LOFFONT,
// EnumFontExDelegate,
// ...
#endregion EnumFont
public static EnumFontExDelegate del1;
public static List<string> EnumFont_Test()
{
fontList.Clear();
LOGFONT lf = CreateLogFont("");
IntPtr plogFont = Marshal.AllocHGlobal(Marshal.SizeOf(lf));
Marshal.StructureToPtr(lf, plogFont, true);
int ret = 0;
try
{
IntPtr hPrinterDC;
StringBuilder dp = new StringBuilder(256);
int size = dp.Capacity;
if (GetDefaultPrinter(dp, ref size))
{
Console.Write(String.Format("Printer: {0}, name length{1}",
dp.ToString().Trim(), size));
}
else
{
Console.WriteLine("Error!");
}
hPrinterDC =
CreateDC("WINSPOOL", dp.ToString(), null, IntPtr.Zero);
IntPtr P = hPrinterDC;
del1 = new EnumFontExDelegate(callback1);
ret = EnumFontFamiliesEx(P, plogFont, del1, IntPtr.Zero, 0);
System.Diagnostics.Trace.WriteLine(
"EnumFontFamiliesEx = " + ret.ToString());
DeleteDC(P);
}
catch
{
System.Diagnostics.Trace.WriteLine("Error!");
}
finally
{
Marshal.DestroyStructure(plogFont, typeof(LOGFONT));
}
return fontList;
}
// Save fonts
static List<string> fontList = new List<string>();
// This is the function that does the extraction of fonts
public static int callback1(
ref ENUMLOGFONTEX lpelfe,
ref NEWTEXTMETRICEX lpntme,
int FontType,
int lParam)
{
try
{
// Do something here.
// This will print out all the fonts.
//Debug.WriteLine(lpelfe.elfFullName);
// For testing purpose, we pretent we are intersted in
// only the ones which start with @.
// Ommit s.StartsWith("@") condition if you want to list all.
string s = lpelfe.elfFullName;
if (s.StartsWith("@") && !fontList.Contains(s))
{
Debug.WriteLine(s);
fontList.Add(s);
}
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine(e.ToString());
}
return 1;
}
#region EnumFont2
// This is from another test func
// http://us.generation-nt.com/answer/
// enumfontfamiliesex-c-help-27455462.htm
//
[DllImport("gdi32.dll")]
static extern IntPtr CreateDC(
string lpszDriver,
string lpszDevice,
string lpszOutput,
IntPtr lpInitData);
[DllImport("gdi32.dll")]
static extern bool DeleteDC(IntPtr hdc);
[DllImport(
"winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetDefaultPrinter(
StringBuilderpszBuffer, ref int size);
#endregion EnumFont2
}
}
Once you have defined this helper class, you can call it from your Revit command like as follows:
// For testing purpose, we are getting only the font
// that starts with "@".
// To get the full list, please modify the EnumFont
// function yourself.
List<string> fontList =
EnumFontFamiliesEx_CS.EnumFont_Test();
fontList.Sort();
foreach (string s in fontList)
{
Debug.WriteLine(s);
}
The result will look like following:
@Arial Unicode MS
@Batang
@BatangChe
@DFKai-SB
@Dotum
@DotumChe
@FangSong
...