Sign in to follow this  
iliak

[.net] Create a System.Drawing.Font from byte[] or Stream

Recommended Posts

Hi In my project, I store all my resources in zip files. I need to create a Font() from a ttf file that is in this Zip file. From what I found, I can only create a System.Drawing.Font from a file on disk. Is it possible to create a Font from a Stream or from a byte[] ??

Share this post


Link to post
Share on other sites
Hi,

Maybe this does what you want:


// loads font family from file
public static FontFamily LoadFontFamily(string fileName) {
using(var pfc = new PrivateFontCollection()) {
pfc.AddFontFile(fileName);
return pfc.Families[0];
}
}

// Load font family from stream
public static FontFamily LoadFontFamily(Stream stream) {
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
return LoadFontFamily(buffer);
}

// load font family from byte array
public static FontFamily LoadFontFamily(byte[] buffer) {
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try {
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
using (var pvc = new PrivateFontCollection()) {
pvc.AddMemoryFont(ptr, buffer.Length);
return pvc.Families[0];
}
} finally {
handle.Free();
}
}





Usage:

For example, from file:

Font theFont = new Font(LoadFontFamily("foobar.ttf"), 25.0f);


Or from resource stream:

var s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Resource.Name.ttf");
Font theFont = new Font(LoadFontFamily(s), 25.0f);


Regards,
Andre

[Edited by - VizOne on July 3, 2008 5:36:44 AM]

Share this post


Link to post
Share on other sites
I thought this would be a great way to support external fonts in my engine but I have hit a problem. When I create a font using the PrivateFontCollection.AddFontFile() method then try to measure the characters I get the following errors

Graphics.MeasureString():
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at System.Drawing.SafeNativeMethods.Gdip.GdipMeasureString(HandleRef graphics, String textString, Int32 length, HandleRef font, GPRECTF& layoutRect, HandleRef stringFormat, GPRECTF& boundingBox, Int32& codepointsFitted, Int32& linesFilled)
at System.Drawing.Graphics.MeasureString(String text, Font font, SizeF layoutArea, StringFormat stringFormat)
at System.Drawing.Graphics.MeasureString(String text, Font font, Int32 width, StringFormat format)

TextRenderer.MeasureText():
Parameter is not valid.
at System.Drawing.FontFamily.GetName(Int32 language)
at System.Drawing.FontFamily.get_Name()
at System.Windows.Forms.Internal.WindowsFont.FromFont(Font font, WindowsFontQuality fontQuality)
at System.Windows.Forms.Internal.WindowsGraphicsCacheManager.GetWindowsFont(Font font, WindowsFontQuality fontQuality)
at System.Windows.Forms.TextRenderer.MeasureText(IDeviceContext dc, String text, Font font, Size proposedSize, TextFormatFlags flags)

Share this post


Link to post
Share on other sites
Nevermind, found the solution, you must keep the PrivateFontCollection object for the life of the font.

So here is my LoadFont() method

private PrivateFontCollection m_pfc = null;

public System.Drawing.Font LoadFont(string fileName, int fontSize, Fontstyle fontstyle)
{
if (File.Exists(fileName))
{
m_pfc = new PrivateFontCollection();
m_pfc.AddFontFile(fileName);

foreach (FontFamily fontFamily in m_pfc.Families)
{
if (fontFamily.IsstyleAvailable(fontstyle))
return new System.Drawing.Font(fontFamily, fontSize, fontstyle, GraphicsUnit.Pixel);
}
}

return new System.Drawing.Font("Arial", fontSize, fontstyle, GraphicsUnit.Pixel);
}


[Edited by - Headkaze on July 5, 2008 11:36:31 PM]

Share this post


Link to post
Share on other sites
Once you loaded the font, what are you doning with it ? Do you make a texture with all glyphs or call gdi++ render functions every time you need to render text ? I try to make a texture with all glyphs but I fail with the method GetGlyphOutline(). Here's my method :


/// <summary>
/// Loads font family from byte array
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
private FontFamily LoadFontFamily(byte[] buffer)
{
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
using (var pfc = new PrivateFontCollection())
{
pfc.AddMemoryFont(ptr, buffer.Length);
return pfc.Families[0];
}
}
finally
{
handle.Free();
}
}




// Open the sdl font
byte[] data = ResourceManager.OpenResource(FileName);
if (data == null)
return false;
Sdl.Font sdlFont = new Sdl.Font(data, Size);
lineHeight = sdlFont.Height + 10;

// GdiFont
Font gdiFont = new Font(LoadFontFamily(data), size, style);

// Final Bitmap
Bitmap bm = new Bitmap(256, LineHeight, PixelFormat.Format32bppArgb);
Bitmap tmpbm = null;

// Position of the glyph in the texture
Point pos = Point.Empty;

// Graphics device
System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bm);
SolidBrush brush = new SolidBrush(color);

//
GLYPHMETRICS metrics = new GLYPHMETRICS();
MAT2 matrix = new MAT2();
matrix.eM11.value = 1;
matrix.eM12.value = 0;
matrix.eM21.value = 0;
matrix.eM22.value = 1;

// Get informations for each glyph
for (byte i = FirstChar; i < LastChar; i++)
{
string c = Convert.ToChar(Convert.ToInt32(i)).ToString();




//
// <errors>
//
// Get glyph informations
IntPtr hdc = gfx.GetHdc();
IntPtr ret = SelectObject(hdc, gdiFont.ToHfont());
uint t = GetGlyphOutline(IntPtr.Zero, i, (uint)2, out metrics, 0, IntPtr.Zero, ref matrix);
gfx.ReleaseHdc();
//
// </errors>
//






// Add the tile to the TileSet
Tile tile = tileSet.AddTile(i);
tile.Rectangle = new Rectangle(pos, sdlFont.SizeText(c));




// Render the glyph to the texture
try
{
gfx.DrawString(c, gdiFont, brush, pos);

}
catch (Exception e)
{

Log.Send(new LogEventArgs(LogLevel.Error, "Error !", e.StackTrace));
}
......


Share this post


Link to post
Share on other sites
I draw each glyph on a texture then use that in my engine. I use TextRenderer.MeasureText() (with TextFormatFlags.NoPadding) to measure each character then Graphics.DrawString to draw each character. Works fine as long as you keep your PrivateFontCollection object alive for the lifetime of the font. Of course as soon as you've finished creating the texture you can dispose of it then.

TextRenderer.MeasureText() works great (much better than the old Graphics.MeasureString()) as it uses GDI instead of GDI+.

Share this post


Link to post
Share on other sites
Quote:
Original post by iliak
Yes, it's really simpler with your method :)

Btw, is it possible to anti alias edges of the font ??

Thanks !


Yes set the following on your texture's bitmap, for example, your texture's bitmap is called m_bitmap.

Graphics g = Graphics.FromImage(m_bitmap);

g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this