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

Started by
8 comments, last by Headkaze 15 years, 9 months ago
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[] ??
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]
Advertisement
Hi,

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]
Andre Loker | Personal blog on .NET
Hi

It's seems to be the good way. I found this url and it uses PrivateFontCollection() too.

Thanks for the code !
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]
Your problem inspired me to blog about it. See this post for more details
Andre Loker | Personal blog on .NET
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)
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]
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));				}......
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]
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+.
Yes, it's really simpler with your method :)

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

Thanks !
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]
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