Text Rendering using Font library | .NET

Overview

If you ever wanted to have the feature of rendering text into an image, the article here will teach you how to do it with fonts of any format, supported by Font library using Aspose.Font API Solution. The library allows you to easily convert text into images as well as add text to any image.

Rendering Text

To insert text into an image you will need to use DrawText Method of the RenderingUtils Class. The code below shows how to add the text “Hello world” written in Arial font to an image named “arial.png”.

 1    var dataDir = @"C:\Temp\";
 2    var fileName = dataDir + "arial.ttf"; //Font file name with full path
 3    var outFile = dataDir + "arial.png";
 4    var fontDefinition = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName)));
 5    var ttfFont = Font.Open(fontDefinition) as TtfFont;
 6
 7    var stream = Aspose.Font.Renderers.RenderingUtils.DrawText(ttfFont, "Hello world", 18);
 8
 9    var bitmap = new Bitmap(stream);
10    bitmap.Save(outFile);

The implementation result:

Hello arial

The DrawText Method allows you to set the line spacing and perform automatic word-by-word text wrapping.

In the code example below, we specified the line spacing type LineSpacingType.Pixels and set it to 10, and we set the maximum width of the image to 450. All text that cannot be displayed correctly in the given range (in our case it is 450) will be wrapped on a new line.

The code snipped shows how to render “Hello world” text in Arial font with wrapping to the next line:

 1    var dataDir = @"C:\Temp\";
 2    var fileName = dataDir + "arial.ttf"; //Font file name with full path
 3    var outFile = dataDir + "arial.png";
 4    var fontDefinition = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName)));
 5    var ttfFont = Font.Open(fontDefinition) as TtfFont;
 6    var lineSpacingType = LineSpacingType.Pixels;
 7
 8    var stream = DrawText(ttfFont, "Hello world", 22, lineSpacingType, 10, 450);
 9
10    var bitmap = new Bitmap(stream);
11
12    bitmap.Save(outFile);

The implementation result:

Hello arial text wrap

In this example, you saw how you can turn text into an image with just one line of code.

The family of functions for the DrawText() Method covers standard text or multiline text output. But, in some cases, you may need a customized output of the text, for example, when you want to compress, stretch, rotate the text at an angle, or something else. In this case, you need to customize the output according to your needs.

Custom text rendering

The next example shows a more advanced (custom) way to convert text to an image.

To draw glyphs within Aspose.Font uses one of RenderGlyph() Methods of GlyphOutlineRenderer Class. All these overloaded methods are declared in the IGlyphRenderer Interface.

As a parameter, we must pass to these methods a reference to the font, glyphId or glyph index, and the glyph output coordinates. To transfer the last parameter, a special matrix represented by the Aspose.Font TransformationMatrix object is used. Below we show how to use objects of the TransformationMatrix type to pass glyph output coordinates to methods of the RenderGlyph() family.

So, to draw a glyph, we need to create an object of the GlyphOutlineRenderer type. But such an object cannot draw a glyph on its own. It requires external functionality, which is described by the IGlyphOutlinePainter Interface. To use the GlyphOutlineRenderer object should be provided with an implementation of the IGlyphOutlinePainter.

Below you can see a simple implementation of this interface. Let’s create the GlyphOutlinePainter Class which requires an object of System.Drawing.Drawing2D.GraphicsPath type for graphic drawing objectives.

The implementation is illustrated below.

 1    public class GlyphOutlinePainter : IGlyphOutlinePainter
 2    {
 3        private GraphicsPath _path;
 4        private PointF _currentPoint;
 5
 6        public GlyphOutlinePainter(GraphicsPath path)
 7        {
 8            _path = path;
 9        }
10
11        public void MoveTo(MoveTo moveTo)
12        {
13            _path.CloseFigure();
14            _currentPoint.X = (float)moveTo.X;
15            _currentPoint.Y = (float)moveTo.Y;
16        }
17
18        public void LineTo(LineTo lineTo)
19        {
20            float x = (float)lineTo.X;
21            float y = (float)lineTo.Y;
22            _path.AddLine(_currentPoint.X, _currentPoint.Y, x, y);
23            _currentPoint.X = x;
24            _currentPoint.Y = y;
25        }
26
27        public void CurveTo(CurveTo curveTo)
28        {
29            float x3 = (float)curveTo.X3;
30            float y3 = (float)curveTo.Y3;
31
32            _path.AddBezier(
33                _currentPoint.X,
34                _currentPoint.Y,
35                (float)curveTo.X1,
36                (float)curveTo.Y1,
37                (float)curveTo.X2,
38                (float)curveTo.Y2,
39                x3,
40                y3);
41
42            _currentPoint.X = x3;
43            _currentPoint.Y = y3;
44        }
45
46        public void ClosePath()
47        {
48            _path.CloseFigure();
49        }
50    }

After the IGlyphOutlinePainter implementation, we can use objects of its type to render an individual glyph by passing them to the GlyphOutlineRenderer and calling the corresponding overloaded methods of the RenderGlyph() family.

In order to render a string of text, we need to get the glyphId for each character and then, using an object of GlyphOutlineRenderer type call one of the methods of the RenderGlyph() family, passing the coordinates of the corresponding glyph.

Let’s take a look at an example of outputting a line of text using the Aspose.Font library. It will be built in the form of the CustomDrawText() Method, which accepts parameters - CustomDrawText(string text, IFont font, double fontSize, Brush backgroundBrush, Brush textBrush, string outFile).

Create the CustomDrawText() Method that draws specified text into the System.Drawing.Bitmap object and saves resultant bitmap on Disc.

This will include the following steps:

Auxillary steps for this strategy

The implementation of the CustomDrawText() Method is shown below.

 1    public static void CustomDrawText(string text, IFont font, double fontSize, Brush backgroundBrush, Brush textBrush, string outFile)
 2    {
 3        //Get glyph identifiers for every symbol in the text line
 4        GlyphId[] gids = new GlyphId[text.Length];
 5
 6        for (int i = 0; i < text.Length; i++)
 7            gids[i] = font.Encoding.DecodeToGid(text[i]);
 8
 9        // Set common drawing settings
10        double dpi = 300;
11        double resolutionCorrection = dpi / 72; // 72 is font's internal dpi
12
13        // Prepare output bitmap                
14        Bitmap outBitmap = new Bitmap(960, 720);
15
16
17        outBitmap.SetResolution((float)dpi, (float)dpi);
18
19        Graphics outGraphics = Graphics.FromImage(outBitmap);
20        outGraphics.FillRectangle(backgroundBrush, 0, 0, outBitmap.Width, outBitmap.Height);
21        outGraphics.SmoothingMode = SmoothingMode.HighQuality;
22
23        //Declare coordinate variables and a previous gid
24        GlyphId previousGid = null;
25        double glyphXCoordinate = 0;
26        double glyphYCoordinate = fontSize * resolutionCorrection;
27
28        // The loop paints every glyph in gids
29        foreach (GlyphId gid in gids)
30        {
31            // if the font contains the gid
32            if (gid != null)
33            {
34                Glyph glyph = font.GlyphAccessor.GetGlyphById(gid);
35                if (glyph == null)
36                    continue;
37
38                // The path that accepts drawing instructions
39                GraphicsPath path = new GraphicsPath();
40
41                // Create IGlyphOutlinePainter implementation
42                GlyphOutlinePainter outlinePainter = new GlyphOutlinePainter(path);
43
44                // Create the renderer
45                Aspose.Font.Renderers.IGlyphRenderer renderer = new Aspose.Font.Renderers.GlyphOutlineRenderer(outlinePainter);
46
47                // Get common glyph properties
48                double kerning = 0;
49
50                // Get kerning value
51
52                if (previousGid != null)
53                {
54                    kerning = (font.Metrics.GetKerningValue(previousGid, gid) / glyph.SourceResolution) * fontSize * resolutionCorrection;
55                    kerning += FontWidthToImageWith(font.Metrics.GetGlyphWidth(previousGid), glyph.SourceResolution, fontSize);
56                }
57
58                // Glyph positioning - increase glyph X coordinate according to kerning distance
59                glyphXCoordinate += kerning;
60
61                // Glyph placement matrix
62                TransformationMatrix glyphMatrix = new TransformationMatrix(
63                        new double[]
64                                { fontSize * resolutionCorrection,
65                            0,
66                            0,
67                        // negative because of the bitmap coordinate system begins from the top
68                            - fontSize*resolutionCorrection,
69                            glyphXCoordinate,
70                            glyphYCoordinate
71                                });
72
73                // Render the current glyph
74                renderer.RenderGlyph(font, gid, glyphMatrix);
75
76                // Fill the path
77                path.FillMode = FillMode.Winding;
78
79                outGraphics.FillPath(textBrush, path);
80            }
81
82            //Set current gid as previous to get correct kerning for next glyph
83            previousGid = gid;
84        }
85
86        //Save the results
87        outBitmap.Save(outFile);
88    }

Utility method to convert font width to image width

1    static double FontWidthToImageWith(double width, int fontSourceResulution, double fontSize, double dpi = 300)
2    {
3        double resolutionCorrection = dpi / 72; // 72 is font's internal dpi
4
5        return (width / fontSourceResulution) * fontSize * resolutionCorrection;
6    }

The next code snippet shows how to render the text “Hello world” using CustomDrawText() Method.

 1    var dataDir = @"C:\Temp\";
 2    var fileName1 = dataDir + "arial.ttf"; //Font file name with full path
 3    var fileName2 = dataDir + "calibrii.ttf"; //Font file name with full path
 4
 5    var fontDefinition1 = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName1)));
 6    var ttfFont1 = Font.Open(fontDefinition1) as TtfFont;
 7
 8    var fontDefinition2 = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName2)));
 9    var ttfFont2 = Font.Open(fontDefinition2) as TtfFont;
10
11    GlyphOutlinePainter.CustomDrawText("Hello world", ttfFont1, 24, Brushes.White, Brushes.Black, dataDir + "Hello_Arial_out.png");
12    GlyphOutlinePainter.CustomDrawText("Hello world", ttfFont2, 24, Brushes.Yellow, Brushes.Blue, dataDir + "Hello_Calibri_out.png");

Implementing the code we will get the following result:

The implementation result:

Hello arial Arial

Hello calibri Calibri

Kerning

Using the value of the kerning variable you can change the distance between glyphs. Now let’s rewrite the code in the next way:

1 
2    //Glyph positioning - increase glyph X coordinate according to the kerning distance
3    kerning *= 1.25;
4    glyphXCoordinate += kerning;

The next result will be gotten:

The implementation result:

Hello arial kerning Arial kernint

Hello calibri kerning Calibri kerning

Rendering text by coordinates

The variables glyphXCoordinate and glyphYCoordinate are responsible for the coordinates of the text output. By changing the code as follows:

1    //Declare coordinate variables and the previous gid
2    GlyphId previousGid = null;
3    double glyphXCoordinate = 300;
4    double glyphYCoordinate = 300;

The next result will be gotten:

The implementation result:

Hello arial kerning Arial x=300 y=300

Hello calibri kerning Calibri x=300 y=300

How to add the text to an image

You can also display text on an existing image. For this, we will rewrite the code as follows:

 1 
 2    public static void CustomDrawText(string text, IFont font, double fontSize, Brush backgroundBrush, Brush textBrush, string outFile, Bitmap bitmap, double kerningCoefficient = 1, double coordinateX = 0, double coordinateY = 0)
 3    {
 4        //Get glyph identifiers for every symbol in the text line
 5        GlyphId[] gids = new GlyphId[text.Length];
 6
 7        for (int i = 0; i < text.Length; i++)
 8            gids[i] = font.Encoding.DecodeToGid(text[i]);
 9
10        // Set common drawing settings
11        double dpi = 300;
12        double resolutionCorrection = dpi / 72; // 72 is font's internal dpi
13
14        // Prepare the output bitmap                
15        Bitmap outBitmap = bitmap;
16
17        outBitmap.SetResolution((float)dpi, (float)dpi);
18
19        Graphics outGraphics = Graphics.FromImage(outBitmap);
20        outGraphics.FillRectangle(backgroundBrush, 0, 0, outBitmap.Width, outBitmap.Height);
21        outGraphics.SmoothingMode = SmoothingMode.HighQuality;
22
23        //Declare coordinate variables and the previous gid
24        GlyphId previousGid = null;
25        double glyphXCoordinate = coordinateX;
26        double glyphYCoordinate = coordinateY;
27
28        glyphYCoordinate += fontSize * resolutionCorrection;
29
30        //The loop paints every glyph in gids
31        foreach (GlyphId gid in gids)
32        {
33            // if the font contains the gid
34            if (gid != null)
35            {
36                Glyph glyph = font.GlyphAccessor.GetGlyphById(gid);
37                if (glyph == null)
38                    continue;
39
40                // The path that accepts drawing instructions
41                GraphicsPath path = new GraphicsPath();
42
43                // Create the IGlyphOutlinePainter implementation
44                GlyphOutlinePainter outlinePainter = new GlyphOutlinePainter(path);
45
46                // Create the renderer
47                Aspose.Font.Renderers.IGlyphRenderer renderer = new Aspose.Font.Renderers.GlyphOutlineRenderer(outlinePainter);
48
49                // Get common glyph properties
50                double kerning = 0;
51
52                // Get the kerning value
53
54                if (previousGid != null)
55                {
56                    kerning = (font.Metrics.GetKerningValue(previousGid, gid) / glyph.SourceResolution) * fontSize * resolutionCorrection;
57                    kerning += FontWidthToImageWith(font.Metrics.GetGlyphWidth(previousGid), glyph.SourceResolution, fontSize);
58                }
59
60                // Glyph positioning - increase the glyph X coordinate according to the kerning distance
61                glyphXCoordinate += kerning * kerningCoefficient;
62
63                // Glyph placement matrix
64                TransformationMatrix glyphMatrix = new TransformationMatrix(
65                        new double[]
66                                { fontSize * resolutionCorrection,
67                            0,
68                            0,
69                        // negative because of the bitmap coordinate system begins from the top
70                            - fontSize*resolutionCorrection,
71                            glyphXCoordinate,
72                            glyphYCoordinate
73                                });
74
75                // Render the current glyph
76                renderer.RenderGlyph(font, gid, glyphMatrix);
77
78                // Fill the path
79                path.FillMode = FillMode.Winding;
80
81                outGraphics.FillPath(textBrush, path);
82            }
83
84            //Set the current gid as previous to get the correct kerning for the next glyph
85            previousGid = gid;
86        }
87
88        //Save the results
89        outBitmap.Save(outFile);
90    }

Let’s modify the way the Method is called:

 1    var dataDir = @"C:\Temp\";
 2    var fileName1 = dataDir + "arial.ttf"; //Font file name with full path
 3
 4    var fontDefinition1 = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName1)));
 5    var ttfFont1 = Font.Open(fontDefinition1) as TtfFont;
 6
 7    var bitmap = new Bitmap(960, 720);
 8
 9    GlyphOutlinePainter.CustomDrawText("Hello world", ttfFont1, 17, Brushes.White, Brushes.Black, dataDir + "Hello_Arial_out.png", bitmap);
10
11    var inputImagePath = dataDir + "Hello_Arial_out.png";
12    var bitmapAddText = new Bitmap(inputImagePath);
13
14    GlyphOutlinePainter.CustomDrawText("Hello world", ttfFont1, 17, Brushes.Transparent, Brushes.Gray, dataDir + "Hello_Arial_Shadow_out.png", bitmapAddText, 1, -3);
15
16    GlyphOutlinePainter.CustomDrawText("<= Shadow effect", ttfFont1, 17, Brushes.Transparent, Brushes.Brown, dataDir + "Hello_Arial_Shadow_out.png", bitmapAddText, 1, 400);

The implementation result:

Hello arial shadow effect Arial shadow effect

Outputting the text from top to bottom

To display the text from top to bottom, let’s make the following changes in the CustomDrawText() Method.

 1    public static void CustomDrawText(string text, IFont font, double fontSize, Brush backgroundBrush, Brush textBrush, string outFile, Bitmap bitmap, double kerningCoefficient = 1, double coordinateX = 0, double coordinateY = 0, bool topDown = false)
 2    {
 3        //Get glyph identifiers for every symbol in the text line
 4        GlyphId[] gids = new GlyphId[text.Length];
 5
 6        for (int i = 0; i < text.Length; i++)
 7            gids[i] = font.Encoding.DecodeToGid(text[i]);
 8
 9        // Set common drawing settings
10        double dpi = 300;
11        double resolutionCorrection = dpi / 72; // 72 is font's internal dpi
12
13        // Prepare the output bitmap                
14        Bitmap outBitmap = bitmap;
15
16        outBitmap.SetResolution((float)dpi, (float)dpi);
17
18        Graphics outGraphics = Graphics.FromImage(outBitmap);
19        outGraphics.FillRectangle(backgroundBrush, 0, 0, outBitmap.Width, outBitmap.Height);
20        outGraphics.SmoothingMode = SmoothingMode.HighQuality;
21
22        //Declare coordinate variables and the previous gid
23        GlyphId previousGid = null;
24        double glyphXCoordinate = coordinateX;
25        double glyphYCoordinate = coordinateY;
26
27        glyphYCoordinate += fontSize * resolutionCorrection;
28
29        //The loop paints every glyph in gids
30        foreach (GlyphId gid in gids)
31        {
32            // if the font contains the gid
33            if (gid != null)
34            {
35                Glyph glyph = font.GlyphAccessor.GetGlyphById(gid);
36                if (glyph == null)
37                    continue;
38
39                // The path that accepts drawing instructions
40                GraphicsPath path = new GraphicsPath();
41
42                // Create IGlyphOutlinePainter implementation
43                GlyphOutlinePainter outlinePainter = new GlyphOutlinePainter(path);
44
45                // Create the renderer
46                Aspose.Font.Renderers.IGlyphRenderer renderer = new Aspose.Font.Renderers.GlyphOutlineRenderer(outlinePainter);
47
48                // Get common glyph properties
49                double kerning = 0;
50
51                // Get kerning value                        
52                if (previousGid != null && !topDown)
53                {
54                    kerning = (font.Metrics.GetKerningValue(previousGid, gid) / glyph.SourceResolution) * fontSize * resolutionCorrection;
55                    kerning += FontWidthToImageWith(font.Metrics.GetGlyphWidth(previousGid), glyph.SourceResolution, fontSize);
56                }
57
58                if (topDown)
59                {
60                    glyphYCoordinate += fontSize * resolutionCorrection;
61                }
62                else
63                {
64                    // Glyph positioning - increase the glyph X coordinate according to kerning distance                       
65                    glyphXCoordinate += kerning * kerningCoefficient;
66                }
67
68                // Glyph placement matrix
69                TransformationMatrix glyphMatrix = new TransformationMatrix(
70                        new double[]
71                                { fontSize * resolutionCorrection,
72                            0,
73                            0,
74                        // negative because the bitmap coordinate system begins from the top
75                            - fontSize*resolutionCorrection,
76                            glyphXCoordinate,
77                            glyphYCoordinate
78                                });
79
80                // Render the current glyph
81                renderer.RenderGlyph(font, gid, glyphMatrix);
82
83                // Fill the path
84                path.FillMode = FillMode.Winding;
85
86                outGraphics.FillPath(textBrush, path);
87            }
88
89            //Set the current gid as previous to get correct kerning for the next glyph
90            previousGid = gid;
91        }
92
93        //Save the results
94        outBitmap.Save(outFile);
95    }

The code for the Method call will have the next look:

1    var dataDir = @"C:\Temp\";
2    var fileName1 = dataDir + "arial.ttf"; //Font file name with full path
3    var fontDefinition1 = new FontDefinition(FontType.TTF, new FontFileDefinition("ttf", new FileSystemStreamSource(fileName1)));
4    var ttfFont1 = Font.Open(fontDefinition1) as TtfFont;
5    var bitmap = new Bitmap(960, 720);
6
7    GlyphOutlinePainter.CustomDrawText("Top down", ttfFont1, 18, Brushes.White, Brushes.Black, dataDir + "Hello_Arial_TopDown.png", bitmap, 1, 400, 00, true);

The implementation result:

Hello arial top to down Arial top to down

Additional Information

1    double dpi; // dots per inch
2    double resolutionCorrection = dpi / 72; // 72 is font's internal dpi
3    // Coordinate variables
4    double glyphXCoordinate; // X
5    double glyphYCoordinate; // Y;
6    // Kerning - horizontal spacing between two letters
7    double kerning = 0;
Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.