/// <summary> /// Renders the <see cref="Duality.Resources.Font"/> based on its embedded TrueType representation. /// <param name="extendedSet">Extended set of characters for renderning.</param> /// </summary> private RenderedFontData RenderGlyphs(byte[] trueTypeFontData, float emSize, FontStyle style, FontCharSet extendedSet, bool antialiasing, bool monospace) { if (this.fontManagers == null) { this.fontManagers = new Dictionary <int, PrivateFontCollection>(); } // Allocate one PrivateFontCollection for each embedded TrueType Font // This is an unfortunate requirement to keep track of which Font is which, // since a byte[] doesn't give it away, and a Font collection won't tell us // which one we just added. PrivateFontCollection manager; int fontId = trueTypeFontData.GetHashCode(); if (!this.fontManagers.TryGetValue(fontId, out manager)) { manager = new PrivateFontCollection(); this.fontManagers.Add(fontId, manager); } // Load custom font family using System.Drawing if (manager.Families.Length == 0) { IntPtr fontBuffer = Marshal.AllocCoTaskMem(trueTypeFontData.Length); Marshal.Copy(trueTypeFontData, 0, fontBuffer, trueTypeFontData.Length); manager.AddMemoryFont(fontBuffer, trueTypeFontData.Length); } // Render the font's glyphs return(this.RenderGlyphs( manager.Families.FirstOrDefault(), emSize, style, extendedSet, antialiasing, monospace)); // Yes, we have a minor memory leak here - both the Font buffer and the private // Font collection. Unfortunately though, GDI+ won't let us dispose them // properly due to aggressive Font caching, see here: // // http://stackoverflow.com/questions/25583394/privatefontcollection-addmemoryfont-producing-random-errors-on-windows-server-20 // // "Standard GDI+ lossage, disposing a Font does not actually destroy it. // It gets put back into a cache, with the assumption that it will be used again. // An important perf optimization, creating fonts is pretty expensive. That ends // poorly for private fonts when you destroy their home, the font will use // released memory. Producing bewildering results, including hard crashes. You'll // need to keep the collection around, as well as the IntPtr." // – Hans Passant Aug 30 '14 at 16:13 }
/// <summary> /// Renders the <see cref="Duality.Resources.Font"/> using the specified system font family. /// </summary> private RenderedFontData RenderGlyphs(FontFamily fontFamily, float emSize, FontStyle style, FontCharSet extendedSet, bool antialiasing, bool monospace) { // Determine System.Drawing font style SysDrawFontStyle systemStyle = SysDrawFontStyle.Regular; if (style.HasFlag(FontStyle.Bold)) { systemStyle |= SysDrawFontStyle.Bold; } if (style.HasFlag(FontStyle.Italic)) { systemStyle |= SysDrawFontStyle.Italic; } // Create a System.Drawing font SysDrawFont internalFont = null; if (fontFamily != null) { try { internalFont = new SysDrawFont(fontFamily, emSize, systemStyle); } catch (Exception e) { Log.Editor.WriteError( "Failed to create System Font '{1} {2}, {3}' for rendering Duality Font glyphs: {0}", Log.Exception(e), fontFamily.Name, emSize, style); } } // If creating the font failed, fall back to a default one if (internalFont == null) { internalFont = new SysDrawFont(FontFamily.GenericMonospace, emSize, systemStyle); } // Render the font's glyphs using (internalFont) { return(this.RenderGlyphs( internalFont, FontCharSet.Default.MergedWith(extendedSet), antialiasing, monospace)); } }
public void Import(IAssetImportEnvironment env) { // Handle all available input. No need to filter or ask for this anymore, as // the preparation step already made a selection with AcceptsInput. We won't // get any input here that didn't match. foreach (AssetImportInput input in env.Input) { // Request a target Resource with a name matching the input ContentRef <DualityFont> targetRef = env.GetOutput <DualityFont>(input.AssetName); // If we successfully acquired one, proceed with the import if (targetRef.IsAvailable) { DualityFont target = targetRef.Res; // Retrieve import parameters float size = env.GetOrInitParameter(targetRef, "Size", 16.0f); FontStyle style = env.GetOrInitParameter(targetRef, "Style", FontStyle.Regular); string extendedCharSet = env.GetOrInitParameter(targetRef, "ExtendedCharSet", string.Empty); bool antialiasing = env.GetOrInitParameter(targetRef, "AntiAlias", true); bool monospace = env.GetOrInitParameter(targetRef, "Monospace", false); // Load the TrueType Font and render all the required glyphs byte[] trueTypeData = File.ReadAllBytes(input.Path); RenderedFontData fontData = this.RenderGlyphs( trueTypeData, size, style, !string.IsNullOrEmpty(extendedCharSet) ? new FontCharSet(extendedCharSet) : null, antialiasing, monospace); // Transfer our rendered Font data to the Font Resource target.SetGlyphData( fontData.Bitmap, fontData.Atlas, fontData.GlyphData, fontData.Metrics); // Add the requested output to signal that we've done something with it env.AddOutput(targetRef, input.Path); } } }
public void Import(IAssetImportEnvironment env) { // Handle all available input. No need to filter or ask for this anymore, as // the preparation step already made a selection with AcceptsInput. We won't // get any input here that didn't match. foreach (AssetImportInput input in env.Input) { // Request a target Resource with a name matching the input ContentRef <DualityFont> targetRef = env.GetOutput <DualityFont>(input.AssetName); // If we successfully acquired one, proceed with the import if (targetRef.IsAvailable) { DualityFont target = targetRef.Res; // Retrieve import parameters float size = env.GetOrInitParameter(targetRef, "Size", 16.0f); FontStyle style = env.GetOrInitParameter(targetRef, "Style", FontStyle.Regular); string customCharSet = env.GetOrInitParameter(targetRef, "CustomCharSet", string.Empty); List <UnicodeBlock> unicodeBlocks = env.GetOrInitParameter(targetRef, "UnicodeBlocks", new List <UnicodeBlock>(DefaultBlocks)); bool antialiasing = env.GetOrInitParameter(targetRef, "AntiAlias", true); bool monospace = env.GetOrInitParameter(targetRef, "Monospace", false); HashSet <char> fullCharSet = new HashSet <char>(); if (!string.IsNullOrWhiteSpace(customCharSet)) { string[] blocks = customCharSet.Split(','); ulong start = 0; ulong end = 0; foreach (string block in blocks) { string[] limits = block.Split(new[] { '-' }, 3); if (!ulong.TryParse(limits[0], NumberStyles.HexNumber, null, out start)) { Log.Editor.WriteError("Cannot parse value " + limits[0] + "; CustomCharSet will be ignored. Please verify the value and repeat the import."); } if (limits.Length == 1) { end = start; } else { if (limits.Length == 2 && !ulong.TryParse(limits[1], NumberStyles.HexNumber, null, out end)) { Log.Editor.WriteError("Cannot parse value " + limits[1] + "; CustomCharSet will be ignored. Please verify the value and repeat the import."); } else if (limits.Length > 2) { Log.Editor.WriteError("Unexpected values " + limits[2] + " in range " + block + " will be ignored. Please verify the value and repeat the import."); } if (start > end) { Log.Editor.WriteWarning(start + " is bigger than " + end + "; block will be ignored. Please verify the value and repeat the import."); } } for (char c = (char)start; c <= (char)end; c++) { if (!char.IsControl(c)) { fullCharSet.Add(c); } } } } if (unicodeBlocks != null) { Type unicodeBlockType = typeof(UnicodeBlock); Type unicodeRangeAttrType = typeof(UnicodeRangeAttribute); foreach (UnicodeBlock block in unicodeBlocks) { UnicodeRangeAttribute range = unicodeBlockType.GetMember(block.ToString()) .First() .GetCustomAttributes(unicodeRangeAttrType, false) .FirstOrDefault() as UnicodeRangeAttribute; if (range != null) { for (char c = (char)range.CharStart; c <= (char)range.CharEnd; c++) { if (!char.IsControl(c)) { fullCharSet.Add(c); } } } } } // Load the TrueType Font and render all the required glyphs byte[] trueTypeData = File.ReadAllBytes(input.Path); RenderedFontData fontData = this.RenderGlyphs( trueTypeData, size, style, new FontCharSet(new string(fullCharSet.ToArray())), antialiasing, monospace); // Transfer our rendered Font data to the Font Resource target.SetGlyphData( fontData.Bitmap, fontData.Atlas, fontData.GlyphData, fontData.Metrics); // Add the requested output to signal that we've done something with it env.AddOutput(targetRef, input.Path); } } }
protected override void ImportResource(ContentRef <DualityFont> resourceRef, AssetImportInput input, IAssetImportEnvironment env) { DualityFont resource = resourceRef.Res; // Retrieve import parameters float size = env.GetOrInitParameter(resourceRef, "Size", 16.0f); FontStyle style = env.GetOrInitParameter(resourceRef, "Style", FontStyle.Regular); string customCharSet = env.GetOrInitParameter(resourceRef, "CustomCharSet", string.Empty); List <UnicodeBlock> unicodeBlocks = env.GetOrInitParameter(resourceRef, "UnicodeBlocks", new List <UnicodeBlock>(DefaultBlocks)); bool antialiasing = env.GetOrInitParameter(resourceRef, "AntiAlias", true); bool monospace = env.GetOrInitParameter(resourceRef, "Monospace", false); HashSet <char> fullCharSet = new HashSet <char>(); if (!string.IsNullOrWhiteSpace(customCharSet)) { string[] blocks = customCharSet.Split(','); ulong start = 0; ulong end = 0; foreach (string block in blocks) { string[] limits = block.Split(new[] { '-' }, 3); if (!ulong.TryParse(limits[0], NumberStyles.HexNumber, null, out start)) { Log.Editor.WriteError("Cannot parse value " + limits[0] + "; CustomCharSet will be ignored. Please verify the value and repeat the import."); } if (limits.Length == 1) { end = start; } else { if (limits.Length == 2 && !ulong.TryParse(limits[1], NumberStyles.HexNumber, null, out end)) { Log.Editor.WriteError("Cannot parse value " + limits[1] + "; CustomCharSet will be ignored. Please verify the value and repeat the import."); } else if (limits.Length > 2) { Log.Editor.WriteError("Unexpected values " + limits[2] + " in range " + block + " will be ignored. Please verify the value and repeat the import."); } if (start > end) { Log.Editor.WriteWarning(start + " is bigger than " + end + "; block will be ignored. Please verify the value and repeat the import."); } } for (char c = (char)start; c <= (char)end; c++) { if (!char.IsControl(c)) { fullCharSet.Add(c); } } } } if (unicodeBlocks != null) { Type unicodeBlockType = typeof(UnicodeBlock); Type unicodeRangeAttrType = typeof(UnicodeRangeAttribute); foreach (UnicodeBlock block in unicodeBlocks) { UnicodeRangeAttribute range = unicodeBlockType.GetMember(block.ToString()) .First() .GetCustomAttributes(unicodeRangeAttrType, false) .FirstOrDefault() as UnicodeRangeAttribute; if (range != null) { for (char c = (char)range.CharStart; c <= (char)range.CharEnd; c++) { if (!char.IsControl(c)) { fullCharSet.Add(c); } } } } } // Load the TrueType Font and render all the required glyphs byte[] trueTypeData = File.ReadAllBytes(input.Path); RenderedFontData fontData = this.RenderGlyphs( trueTypeData, size, style, new FontCharSet(new string(fullCharSet.ToArray())), antialiasing, monospace); // Transfer our rendered Font data to the Font Resource resource.SetGlyphData( fontData.Bitmap, fontData.Atlas, fontData.GlyphData, fontData.Metrics); }
/// <summary> /// Renders the <see cref="Duality.Resources.Font"/> using the specified system font family. /// </summary> private RenderedFontData RenderGlyphs(FontFamily fontFamily, float emSize, FontStyle style, FontCharSet extendedSet, bool antialiasing, bool monospace) { // Determine System.Drawing font style SysDrawFontStyle systemStyle = SysDrawFontStyle.Regular; if (style.HasFlag(FontStyle.Bold)) systemStyle |= SysDrawFontStyle.Bold; if (style.HasFlag(FontStyle.Italic)) systemStyle |= SysDrawFontStyle.Italic; // Create a System.Drawing font SysDrawFont internalFont = null; if (fontFamily != null) { try { internalFont = new SysDrawFont(fontFamily, emSize, systemStyle); } catch (Exception e) { Log.Editor.WriteError( "Failed to create System Font '{1} {2}, {3}' for rendering Duality Font glyphs: {0}", Log.Exception(e), fontFamily.Name, emSize, style); } } // If creating the font failed, fall back to a default one if (internalFont == null) internalFont = new SysDrawFont(FontFamily.GenericMonospace, emSize, systemStyle); // Render the font's glyphs using (internalFont) { return this.RenderGlyphs( internalFont, FontCharSet.Default.MergedWith(extendedSet), antialiasing, monospace); } }
/// <summary> /// Renders the <see cref="Duality.Resources.Font"/> based on its embedded TrueType representation. /// <param name="extendedSet">Extended set of characters for renderning.</param> /// </summary> private RenderedFontData RenderGlyphs(byte[] trueTypeFontData, float emSize, FontStyle style, FontCharSet extendedSet, bool antialiasing, bool monospace) { if (this.fontManagers == null) this.fontManagers = new Dictionary<int, PrivateFontCollection>(); // Allocate one PrivateFontCollection for each embedded TrueType Font // This is an unfortunate requirement to keep track of which Font is which, // since a byte[] doesn't give it away, and a Font collection won't tell us // which one we just added. PrivateFontCollection manager; int fontId = trueTypeFontData.GetHashCode(); if (!this.fontManagers.TryGetValue(fontId, out manager)) { manager = new PrivateFontCollection(); this.fontManagers.Add(fontId, manager); } // Load custom font family using System.Drawing if (manager.Families.Length == 0) { IntPtr fontBuffer = Marshal.AllocCoTaskMem(trueTypeFontData.Length); Marshal.Copy(trueTypeFontData, 0, fontBuffer, trueTypeFontData.Length); manager.AddMemoryFont(fontBuffer, trueTypeFontData.Length); } // Render the font's glyphs return this.RenderGlyphs( manager.Families.FirstOrDefault(), emSize, style, extendedSet, antialiasing, monospace); // Yes, we have a minor memory leak here - both the Font buffer and the private // Font collection. Unfortunately though, GDI+ won't let us dispose them // properly due to aggressive Font caching, see here: // // http://stackoverflow.com/questions/25583394/privatefontcollection-addmemoryfont-producing-random-errors-on-windows-server-20 // // "Standard GDI+ lossage, disposing a Font does not actually destroy it. // It gets put back into a cache, with the assumption that it will be used again. // An important perf optimization, creating fonts is pretty expensive. That ends // poorly for private fonts when you destroy their home, the font will use // released memory. Producing bewildering results, including hard crashes. You'll // need to keep the collection around, as well as the IntPtr." // – Hans Passant Aug 30 '14 at 16:13 }