Get Supported Characters of a Font - in C#

Enumerate supported characters/glyphs in a specific font with UWP/C#

New Answer - Checking custom font files

You can check if a font maps a glyph to particular Unicode character/code via DirectWrite using the IDWriteFontFace::GetGlyphIndices method. There is a C# wrapper for DirectWrite as part of the SharpDx library, specifically the SharpDX.Direct2D1 package on nuget.

I have made an example from the SharpDx Directwrite example on github. I used a file selector dialog to open the files since UWP apps are not allowed to access most of the file system unless the user selects the files themselves. You can also put the fonts with your app. I downloaded two free fonts to test, "Aileron" and "Grundschrift". I just whipped it up really fast to show you its not that hard, but the code doesn't follow best practices I am sure. First, add the following three classes from the SharpDx Directwrite custom font example: ResourceFontFileStream.cs, ResourceFontLoader.cs, and ResourceFontFileEnumerator.cs. Change the namespaces to your project namespace. In ResourceFontLoader.cs change the constructor to this:

public ResourceFontLoader(Factory factory, List<Stream> fontfiles)
{
_factory = factory;
var AnyFontsLoaded = false;
foreach (var filename in fontfiles)
{
try
{
using (filename)
{
var fontBytes = Utilities.ReadStream(filename);
var stream = new DataStream(fontBytes.Length, true, true);
stream.Write(fontBytes, 0, fontBytes.Length);
stream.Position = 0;
_fontStreams.Add(new ResourceFontFileStream(stream));
AnyFontsLoaded = true;
}
}
catch (System.Exception)
{
// Handle all file exceptions how you see fit
throw;
}
}
if (AnyFontsLoaded)
{
// Build a Key storage that stores the index of the font
_keyStream = new DataStream(sizeof(int) * _fontStreams.Count, true, true);
for (int i = 0; i < _fontStreams.Count; i++)
_keyStream.Write((int)i);
_keyStream.Position = 0;

// Register the
_factory.RegisterFontFileLoader(this);
_factory.RegisterFontCollectionLoader(this);
}
}

In your testing main page:

public sealed partial class MainPage : Page
{
public ResourceFontLoader CurrentResourceFontLoader { get; set; }
public FontCollection CurrentFontCollection { get; set; }
public SharpDX.DirectWrite.Factory FactoryDWrite { get; private set; }
public List<Stream> customFontStreams { get; set; }
public List<string> FontFamilyNames { get; set; }
public MainPage()
{
customFontStreams = new List<Stream>();
this.InitializeComponent();
}

async Task LoadCustomFonts()
{
await Task.Run(() =>
{
// Font Families
FontFamilyNames = new List<string> { "Aileron", "Grundschrift" };
// Character codes to check for:
int[] codes = { 0x41, 0x6f, 0x7c, 0xc2aa, 0xD7A3 };
FactoryDWrite = new SharpDX.DirectWrite.Factory();
CurrentResourceFontLoader = new ResourceFontLoader(FactoryDWrite, customFontStreams);
CurrentFontCollection = new FontCollection(FactoryDWrite, CurrentResourceFontLoader, CurrentResourceFontLoader.Key);

foreach (var fontfamilyname in FontFamilyNames)
{
int familyIndex;
CurrentFontCollection.FindFamilyName(fontfamilyname, out familyIndex);

using (var fontFamily = CurrentFontCollection.GetFontFamily(familyIndex))
{
var font = fontFamily.GetFont(0);

using (var fontface = new FontFace(font))
{
var results = fontface.GetGlyphIndices(codes);
for (int i = 0; i < codes.Length - 1; i++)
{
if (results[i] > 0)
{
Debug.WriteLine("Contains the unicode character " + codes[i]);

}
else
{
Debug.WriteLine("Does not contain the unicode character " + codes[i]);
}
}
}
}

}
});

}
private async void button_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.List;
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".otf");
openPicker.FileTypeFilter.Add(".ttf");
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
if (files.Count > 0)
{

// Application now has read/write access to the picked file(s)
foreach (StorageFile file in files)
{
customFontStreams.Add(await file.OpenStreamForReadAsync());
}
await LoadCustomFonts();
}
}
}


Original Answer - Checking system fonts:

An example using the SharpDX.Direct2D1 package to check if a font has certain characters:

var fontName = "Segoe UI";

using (var factory = new Factory(FactoryType.Shared))
{
using (var fontCollection = factory.GetSystemFontCollection(true))
{
int familyIndex;
fontCollection.FindFamilyName(fontName, out familyIndex);

using (var fontFamily = fontCollection.GetFontFamily(familyIndex))
{
var font = fontFamily.GetFont(0);

using (var fontface = new FontFace(font))
{
int[] codes = { 0x41, 0x6f, 0x7c, 0xc2aa, 0xD7A3 };

var results = fontface.GetGlyphIndices(codes);
for (int i = 0; i < codes.Length - 1; i++)
{
if (results[i] > 0)
{
Debug.WriteLine("Contains the unicode character " + codes[i]);
}
else
{
Debug.WriteLine("Does not contain the unicode character " + codes[i]);
}
}
}
}

}
}

How to determine which fonts contain a specific character?

I've tested this on .NET 4.0, you need to add reference to PresentationCore to get the font & typeface types to work. Also check Fonts.GetFontFamilies overloads.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Markup;
using System.Windows.Media;

class Program
{
public static void Main(String[] args)
{
PrintFamiliesSupportingChar('a');
Console.ReadLine();
PrintFamiliesSupportingChar('â');
Console.ReadLine();
PrintFamiliesSupportingChar('嗎');
Console.ReadLine();
}

private static void PrintFamiliesSupportingChar(char characterToCheck)
{
int count = 0;
ICollection<FontFamily> fontFamilies = Fonts.GetFontFamilies(@"C:\Windows\Fonts\");
ushort glyphIndex;
int unicodeValue = Convert.ToUInt16(characterToCheck);
GlyphTypeface glyph;
string familyName;

foreach (FontFamily family in fontFamilies)
{
var typefaces = family.GetTypefaces();
foreach (Typeface typeface in typefaces)
{
typeface.TryGetGlyphTypeface(out glyph);
if (glyph != null && glyph.CharacterToGlyphMap.TryGetValue(unicodeValue, out glyphIndex))
{
family.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out familyName);
Console.WriteLine(familyName + " supports ");
count++;
break;
}
}
}
Console.WriteLine();
Console.WriteLine("Total {0} fonts support {1}", count, characterToCheck);
}
}

How To: Determine which character ranges are supported by a font

You would need to P/Invoke GetFontUnicodeRanges(). Not easy to do because the font needs to be selected in the device context, that requires more p/invoke.

But most of all, it isn't the right thing to do. You should rely on Windows' automatic font linking, it finds another font if necessary to supply the glyph. The feature is described in this article.

Referencing symbols of a font that don't have a unicode in c#

I ended up using the fantastic tool fontello and uploaded the open source icon font, selected all the symbols from it and downloaded it again - which generated new unicodes for every single symbol and i got it to work like a charm now. (Additionally fontello generates a great spreadsheet showing every code for every symbol).

In the end i guess this is the smartest way to work around this - if you need to access symbols from a font via unicode but the font isn't laid-out for this, use a tool like fontello to generate your own.

Finding out what unicode characters are supported by a font in .NET 2.0

Yes, that's what you'll need to pinvoke. Graphics.GetHdc() gets you a device context handle. Font.ToHfont() gets you a font handle. Pinvoke SelectObject to select the font into the device context and its ready for use. Be sure to restore everything when you're done. Visit pinvoke.net for the declarations you'll need.

Is is possible to determine the character set of a Font?

You should use the GetTextMetrics function (https://msdn.microsoft.com/en-us/library/windows/desktop/dd144941(v=vs.85).aspx). When called using interop, it retrieves the following structure:

[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)]
internal struct TEXTMETRIC
{
public int tmHeight;
public int tmAscent;
public int tmDescent;
public int tmInternalLeading;
public int tmExternalLeading;
public int tmAveCharWidth;
public int tmMaxCharWidth;
public int tmWeight;
public int tmOverhang;
public int tmDigitizedAspectX;
public int tmDigitizedAspectY;
public char tmFirstChar;
public char tmLastChar;
public char tmDefaultChar;
public char tmBreakChar;
public byte tmItalic;
public byte tmUnderlined;
public byte tmStruckOut;
public byte tmPitchAndFamily;
public byte tmCharSet;
}

The last member of the struct, tmCharSet, is what you are looking for:

The character set of the font. The character set can be one of the
following values: ANSI_CHARSET BALTIC_CHARSET CHINESEBIG5_CHARSET
DEFAULT_CHARSET EASTEUROPE_CHARSET GB2312_CHARSET GREEK_CHARSET
HANGUL_CHARSET MAC_CHARSET OEM_CHARSET RUSSIAN_CHARSET
SHIFTJIS_CHARSET SYMBOL_CHARSET TURKISH_CHARSET VIETNAMESE_CHARSET ...

Following this link, you can find a simple implementation of the necessary code.



Related Topics



Leave a reply



Submit