3

QUESTION

In C# or VB.NET, under WinForms class library or WPF, and without requiring 3rd party applications or APIs, I would like to know how to retrieve the title of a text font file, in the same way as it is shown when doing right click on a text font file and going to the details property page.

In other words, I need to obtain the exact name as which the font will be registered in the Windows Registry, which is not the same name as gives me the System.Drawing.FontFamily.Name property neither the System.Drawing.Font.Name property.

RESEARCH

There are some questions in StackOverflow about how to get the font name, like this and this, but none for the font title.

Nothing of that could help me. I'll give an example of the difference:

I have a true type font file with file name "OpenSans-Light.ttf", which in the details property page is shown as "Open Sans Light", but using a System.Drawing.FontFamily based solution i get the name "Open Sans".

Then, after I've seen that maybe .NET Framework (WindowsForms) class library does not provide a functionality to obtain the font title, I tried to discover which function from the Windows API could be using the windows shell extension of the file details property sheet to obtain the font title...

...I found nothing about it, no information, nothing of nothing, just the GetFontData function which does not provides me the info that I need.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • For "Open Sans Light', shouldn't that be Font.Name + Font.Style? – Camilo Terevinto Nov 03 '18 at 18:20
  • @Camilo Terevinto that is the first thing I thought too when I was trying to think about how it is build the font title, except that there is no "Light" font style so it can't be. Thank you for comment. – ElektroStudios Nov 03 '18 at 18:22
  • My bad, I meant Font.Weight – Camilo Terevinto Nov 03 '18 at 18:23
  • @Camilo Terevinto Sorry but also there is no existing System.Drawing.Font.Weight property. – ElektroStudios Nov 03 '18 at 18:30
  • Huh, I was looking at `System.Windows`... https://stackoverflow.com/questions/25249167/windows-forms-font-weight – Camilo Terevinto Nov 03 '18 at 18:33
  • @Camilo Terevinto Please provide a full namespace to the class that provides that "Weight" field or property, or namespace for the supposed enumeration that contains a flag with name "Light" which you are talking about. From what I can see, System.Windows.Media.FontFamily class does not provide a kind of Weight property, and I don't see (better said, I'm not aware of) an equivalent of System.Drawing.Font class for WPF. – ElektroStudios Nov 03 '18 at 19:11
  • 1
    In `System.Windows.Media.Fonts` you have `SystemTypefaces`, which will return all installed Font. Similar to `System.Windows.Text.InstalledFontCollection`. This can give you some informations. Much more from the `SystemTypefaces.TypeFace` object. See the `TypeFace.TryGetGlyphTypeface(out [GlyphTypeface]);`. (you need to instantiate a new `System.Windows.Media.GlyphTypeface` beforehand). This will give you a lot of useful information, including the `..\Windows\Fonts` font file URI and all the metrics, licensing etc. Some *fancy* names given to the Font weight are just Foundries file names. – Jimi Nov 03 '18 at 21:04
  • 1
    Some other, very specific (numbers), informations, can be reached through reflection (some Types are not public: `MS.Internal.{}` types). – Jimi Nov 03 '18 at 21:05
  • 1
    Forgot to mention that [Fonts.GetTypefaces](https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.fonts.gettypefaces) can be used to retrieve informations on Fonts that are not installed (private repository/folder). – Jimi Nov 03 '18 at 21:13
  • @Jimi Thanks, I think I could be near a solution by combining the GlyphTypeface.Win32FamilyNames and GlyphTypeface.Win32FaceNames properties, it seems to be the same string generated by Windows, but I'm not really sure until I test the resulting names with thousands of font files to see whether all matches the same string. But... – ElektroStudios Nov 04 '18 at 00:25
  • But I have some doubts before I continue, maybe you can help me?: 1.Do you know an equivalent for **Fonts.GetTypefaces** just to analyze a single file instead of passing a directory to analyze all files?. 2.Why I'm getting like 4 Typefaces for each of the font files?,how can I determine which one is the correct to analyze?. 3.The properties **Win32FamilyNames** and **Win32FaceNames** takes a **CultureInfo** as argument,I have same doubt as second question,how can I ensure those properties always will return me a non-empty string if the default typeface culture is not "en-US"?(or it cannot be?) – ElektroStudios Nov 04 '18 at 00:27
  • I got it !!, this approach about WinForms or WPF font classes representation that I was willing to bet on, is tedious, probably the result would not be completely reliable/efficient and I have discovered the correct way, or at least a 100% reliable to achieve the same font name string as Windows generates when registering the font. I will publish soon an answer with the solution. – ElektroStudios Nov 04 '18 at 00:49
  • 1
    2) A single Font. A Font is made up of more files. Each Style (Normal, Italic, Bold, Bold-Italic etc.) is a different File (even though in the Fonts repository you just see the FamilyName). Helvetica (the standard Adobe pack) has 1 `FamilyName` and 35 `FontFamily.FamilyTypefaces`. This also answer 1) You most of the time need a Directory to contain all the Typefaces (it could be just 1 file, in some cases, but you do that as a procedure). 3) `Win32FamilyNames` should be always english. Pass `CultureInfo.InvariantCulture`. `Win32FaceNames` contains the **Styles** names in different languages. – Jimi Nov 04 '18 at 00:53
  • 1
    ... Thus, when trying the Local `CultureInfo` to retrieve the localized Style name, returns null, you just get the `InvariantCulture` reference. You can use the same procedure form both the indexed properties `Win32FamilyNames` and `Win32FaceNames`. – Jimi Nov 04 '18 at 01:02
  • @Jimi Really thank you for your help, I always can learn something new, slowly I discover new built-in useful classes inside the WPF class library, and what you've explained about a font structure, thanks again. The only "problem",if it can be considered as a problem for the needs of someone with the solution I published, is that the font style name that I get ("bold", "italic", etc) are in English instead of the OS language(I comment it because I see many font styles of the default built-in Windows fonts in the OS language, non-English, in the Windows registry key where fonts are registered) – ElektroStudios Nov 04 '18 at 01:28
  • 1
    `Win32FaceNames` holds an indexed list (a `Dictionary`) of style names in many languages (well, not that many). Spanish is there (IIRC, it always has been). Get it with `string StyleName = [GlyphTypeface].Win32FaceNames[new CultureInfo("es-ES")];` – Jimi Nov 04 '18 at 01:37
  • Yes I was aware of that, thanks. I'll retract from what I said, WindowsAPICodePack returns the style (bold, etc) by default in the OS language (in my case Spanish), but this does not occur for all the fonts, I don't know why, then it returns others in English, so by default it 100% generates the same string shown in Windows registry. And this means that the real "problem" is that using WindowsAPICodePack solution it seems I can't decide which culture to use for retrieving the style. Maybe at the end I should mix what I have, with your solution, if I always want to get a English string. – ElektroStudios Nov 04 '18 at 01:41
  • 1
    Nobody forbids you to use the best of both worlds :) – Jimi Nov 04 '18 at 01:43
  • @Jimi Sorry for bothering you but I'm getting crazy with this,I don't understand why some font title's styles are shown in English,and some others in Spanish.I mean in the Explorer list view,in the "Title" column (same title that I get with the code that I published,some are in English,others in Spanish).This could mean maybe the title is just metadata stored in the font file that can be in whatever language,instead of a specific OS function that builds the font title?,because otherwise I don't understand why "bold","italic" etc. isn't always shown in English or in Spanish, one of two, always. – ElektroStudios Nov 04 '18 at 01:53
  • 1
    Some Font have the Weight included in the Title (the Font Face name). Something like Gothic, Grotesk, BlackItalic etc . Or the destination: Display, Book, Text etc. Those are part of the Font Name. Foundries get somewhat obsessed when naming their Fonts. And it's part of the Foundry *style*. When a FontFaceNames style is available, Windows adds it to the Font Face name (Normal, Bold, Italic etc) in the Local language. Sometimes they're not specified because the Font has a *destination* that doesn't imply a standard weight. – Jimi Nov 04 '18 at 02:11
  • 1
    Also, note that, many times, the Fonts are just converted from Adobe Type 1 (pfm) composite files to Windows OTF or TTF. Some internal informations are just *built* by the conversion software. Which has its style, too. – Jimi Nov 04 '18 at 02:17

1 Answers1

3

I noticed that the "Title" column in the Explorer list view shows the exact font name string that I need, and so knowing that, then I realized that the problem can be easily solved by using the win32 shell property wrapper of the WindowsAPICodePack library to retrieve the Title property of a font file.

I'm aware that I requested a solution without 3rd party libraries, however I'm almost sure it will not be a more ideal solution than using this, because... well, the consistent alternative seems to implement the Win32 shell property wrapper ourselves.

A sample code:

Imports Microsoft.WindowsAPICodePack.Shell

Dim diInfo As New DirectoryInfo("C:\Fonts\")

For Each fiInfo As FileInfo In diInfo.GetFiles("*.ttf", SearchOption.TopDirectoryOnly)
    Dim sFile As ShellFile = ShellFile.FromFilePath(fiInfo.FullName)
    Dim title As String = sFile.Properties.System.Title.Value

    Dim sb As New StringBuilder()
    sb.AppendLine(String.Format("Name.: {0}", fiInfo.Name))
    sb.AppendLine(String.Format("Title: {0}", title))

    Console.WriteLine(sb.ToString())
Next

An example output:

Name.: OpenSans LightItalic.ttf
Title: Open Sans Light Italic

Name.: OpenSans Light_0.ttf
Title: Open Sans Light

Name.: OpenSans-Bold.ttf
Title: Open Sans Bold

Name.: OpenSans-BoldItalic.ttf
Title: Open Sans Bold Italic

Name.: OpenSans-ExtraBold.ttf
Title: Open Sans Extrabold

Name.: OpenSans-ExtraBoldItalic.ttf
Title: Open Sans Extrabold Italic

Name.: OpenSans-Italic.ttf
Title: Open Sans Italic

Name.: OpenSans-Light.ttf
Title: Open Sans Light

Name.: OpenSans-LightItalic.ttf
Title: Open Sans Light Italic

Name.: OpenSans-Regular.ttf
Title: Open Sans

Name.: OpenSans-Semibold.ttf
Title: Open Sans Semibold

Name.: OpenSans-SemiboldItalic.ttf
Title: Open Sans Semibold Italic

The only missing thing is to add "(TrueType)" or "(OpenType)" at the end of the string if we really need the exact same string as in the Windows Registry is shown (in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts key).

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417