public override SpriteFontContent Process(FontDescription input,
                                                  ContentProcessorContext context)
        {
            var output = new SpriteFontContent(input);
            var font   = new Font(input.FontName, input.Size);

            // Make sure that this font is installed on the system.
            // Creating a font object with a font that's not contained will default to MS Sans Serif:
            // http://msdn.microsoft.com/en-us/library/zet4c3fa.aspx
            if (font.FontFamily.Name == "Microsoft Sans Serif" && input.FontName != "Microsoft Sans Serif")
            {
                throw new PipelineException(string.Format("Font {0} is not installed on this computer.", input.FontName));
            }

            var estimatedSurfaceArea = 0;
            var largestHeight        = 0;
            var widthsAndHeights     = new List <Point>();

            // Estimate the bounds of each rect to calculate the
            // final texture size
            var sf = StringFormat.GenericTypographic;

            sf.Trimming    = StringTrimming.None;
            sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

            using (var bmp = new Bitmap((int)(font.Size), (int)(font.Size)))
                using (var temp = System.Drawing.Graphics.FromImage(bmp))
                {
                    // Calculate and save the size of each character
                    foreach (var ch in input.Characters)
                    {
                        var charSize = temp.MeasureString(ch.ToString(), font, new PointF(0, 0), sf);
                        var width    = (int)charSize.Width;
                        var height   = (int)charSize.Height;

                        estimatedSurfaceArea += width;
                        largestHeight         = Math.Max(largestHeight, height);

                        widthsAndHeights.Add(new Point(width, height));
                    }

                    // TODO: Using the largest height will give us some empty space
                    // This can be optimized to pack a smaller texture if necessary
                    estimatedSurfaceArea *= largestHeight;
                }

            output.VerticalLineSpacing = largestHeight;

            // calculate the best height and width for our output texture.
            // TODO: GetMonoGamePlatform()
            var texBounds = calculateOutputTextureBounds(estimatedSurfaceArea, true);

            // Create our texture
            var outputBitmap = new Bitmap(texBounds.X, texBounds.Y);

            using (var g = System.Drawing.Graphics.FromImage(outputBitmap))
            {
                g.FillRectangle(Brushes.Transparent, new System.Drawing.Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height));

                int x = 0;
                int y = 0;
                // Draw each glyph into the image.
                for (int i = 0; i < input.Characters.Count; i++)
                {
                    var kernData  = FontHelper.GetCharWidthABC(input.Characters[i], font, g);
                    int charWidth = (int)(Math.Abs(kernData.abcA) + kernData.abcB + kernData.abcC);

                    if (!input.UseKerning)
                    {
                        charWidth = (int)kernData.abcB;
                    }

                    if (x + charWidth >= texBounds.X)
                    {
                        x  = 0;
                        y += largestHeight;
                    }

                    Rectangle rect = new Microsoft.Xna.Framework.Rectangle(x, y, charWidth, widthsAndHeights[i].Y);
                    output.Glyphs.Add(rect);

                    // Characters with a negative a kerning value (like j) need to be adjusted,
                    // so (in the case of j) the bottom curve doesn't render outside our source
                    // rect.
                    var renderPoint = new PointF(x, y);
                    if (kernData.abcA < 0)
                    {
                        renderPoint.X += Math.Abs(kernData.abcA);
                    }

                    g.DrawString(input.Characters[i].ToString(), font, Brushes.White, renderPoint, sf);
                    output.Cropping.Add(new Rectangle(0, 0, charWidth, output.Glyphs[i].Height));

                    if (!input.UseKerning)
                    {
                        kernData.abcA = 0;
                        kernData.abcC = 0;
                    }
                    output.Kerning.Add(new Vector3(kernData.abcA, kernData.abcB, kernData.abcC));

                    // Add a 2 pixel spacing between characters
                    x += charWidth + 2;
                }

                // Drawing against a transparent black background blends
                // the 'alpha' pixels against black, leaving a black outline.
                // Interpolate between black and white
                // based on it's intensity to covert this 'outline' to
                // it's grayscale equivalent.
                var transBlack = Color.TransparentBlack;
                for (var i = 0; i < outputBitmap.Width; i++)
                {
                    for (var j = 0; j < outputBitmap.Height; j++)
                    {
                        var px = outputBitmap.GetPixel(i, j);

                        if (px.ColorsEqual(transBlack))
                        {
                            continue;
                        }

                        var val = (px.R + px.B + px.G) / (255.0f * 3.0f);
                        var col = Color.Lerp(Color.Transparent, Color.White, val);
                        px = System.Drawing.Color.FromArgb(col.A, col.R, col.G, col.B);
                        outputBitmap.SetPixel(i, j, px);
                    }
                }

                var bitmapContent = new PixelBitmapContent <Color>(texBounds.X, texBounds.Y);
                bitmapContent.SetPixelData(outputBitmap.GetData());
                output.Texture.Faces.Add(new MipmapChain(bitmapContent));
            }

            return(output);
        }