Example #1
0
        /// <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
        }
Example #2
0
        /// <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));
            }
        }
Example #3
0
        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);
                }
            }
        }
Example #4
0
        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);
                }
            }
        }
Example #5
0
        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);
        }
Example #6
0
        /// <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);
            }
        }
Example #7
0
        /// <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
        }