[.net] Create a System.Drawing.Font from byte[] or Stream
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[] ??
Hi,
Maybe this does what you want:
Usage:
For example, from file:
Or from resource stream:
Regards,
Andre
[Edited by - VizOne on July 3, 2008 5:36:44 AM]
Maybe this does what you want:
// loads font family from filepublic static FontFamily LoadFontFamily(string fileName) { using(var pfc = new PrivateFontCollection()) { pfc.AddFontFile(fileName); return pfc.Families[0]; }}// Load font family from streampublic 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 arraypublic 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]
Hi
It's seems to be the good way. I found this url and it uses PrivateFontCollection() too.
Thanks for the code !
It's seems to be the good way. I found this url and it uses PrivateFontCollection() too.
Thanks for the code !
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)
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)
Nevermind, found the solution, you must keep the PrivateFontCollection object for the life of the font.
So here is my LoadFont() method
[Edited by - Headkaze on July 5, 2008 11:36:31 PM]
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]
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)); }......
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+.
TextRenderer.MeasureText() works great (much better than the old Graphics.MeasureString()) as it uses GDI instead of GDI+.
Yes, it's really simpler with your method :)
Btw, is it possible to anti alias edges of the font ??
Thanks !
Btw, is it possible to anti alias edges of the font ??
Thanks !
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;
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement