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 example below renders “Hello world” in Verdana and saves the returned PNG stream as Text.png.
Set DataDir to the directory containing TTF/verdana.ttf and TTF/LatoWeb-Regular.ttf, and set OutputDir to an existing output directory. The examples use Aspose.Font, Aspose.Font.Sources, Aspose.Font.Renderers, System and System.IO. In the test project, these directories and the license setup are supplied by BaseTests.
1// Render text to a PNG image
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string text = "Hello world";
6string outFile = Path.Combine(OutputDir, "Text.png");
7using (Stream image = RenderingUtils.DrawText(font, text, 18))
8{
9 image.Position = 0;
10 using FileStream output = File.Create(outFile);
11 image.CopyTo(output);
12}The implementation result:

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 example repeats “Hello world” in Verdana so that the text exceeds the 450-pixel width and wraps onto additional lines. The result is saved as WrappedText.png:
1// Render wrapped text with pixel line spacing
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string text = "Hello world Hello world Hello world Hello world Hello world Hello world";
6string outFile = Path.Combine(OutputDir, "WrappedText.png");
7using (Stream image = RenderingUtils.DrawText(font, text, 22, RenderingUtils.LineSpacingType.Pixels, 10, 450))
8{
9 image.Position = 0;
10 using FileStream output = File.Create(outFile);
11 image.CopyTo(output);
12}The implementation result:

DrawText() returns a PNG stream. The examples rewind and copy that stream to a file, then dispose both streams.
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 following examples render glyph outlines using System.Drawing.Common on Windows. They use a 960 x 720 canvas at 300 DPI. In addition to the namespaces above, import Aspose.Font.Glyphs, Aspose.Font.Rendering, Aspose.Font.RenderingPath, System.Drawing, System.Drawing.Drawing2D and System.Drawing.Imaging.
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 interface implementation is shown below. Add the CustomDrawText() overloads and FontWidthToImageWidth() method shown later as members of this GlyphOutlinePainter class. The example project places these members in a second partial declaration of the same class.
1// Implement a glyph outline painter
2
3public partial class GlyphOutlinePainter : IGlyphOutlinePainter
4{
5 private readonly GraphicsPath path;
6 private PointF currentPoint;
7
8 public GlyphOutlinePainter(GraphicsPath path)
9 {
10 this.path = path;
11 }
12
13 public void MoveTo(MoveTo moveTo)
14 {
15 path.CloseFigure();
16 currentPoint = new PointF((float)moveTo.X, (float)moveTo.Y);
17 }
18
19 public void LineTo(LineTo lineTo)
20 {
21 var next = new PointF((float)lineTo.X, (float)lineTo.Y);
22 path.AddLine(currentPoint, next);
23 currentPoint = next;
24 }
25
26 public void CurveTo(CurveTo curveTo)
27 {
28 var next = new PointF((float)curveTo.X3, (float)curveTo.Y3);
29 path.AddBezier(currentPoint, new PointF((float)curveTo.X1, (float)curveTo.Y1),
30 new PointF((float)curveTo.X2, (float)curveTo.Y2), next);
31 currentPoint = next;
32 }
33
34 public void ClosePath()
35 {
36 path.CloseFigure();
37 }
38}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:
- Iterate all symbols in a text string.
- Get glyph identifier for every processed symbol - gid.
- Create an object of GlyphOutlinePainter type which is required by the rendering subsystem to draw the current glyph.
- Create an object of the Aspose.Font.Renderers.GlyphOutlineRenderer type, and pass the just created object of GlyphOutlinePainter type into the constructor for GlyphOutlineRenderer. This GlyphOutlineRenderer object is intended to render specified glyphs.
- Render the current glyph using GlyphOutlineRenderer.RenderGlyph(). A TransformationMatrix specifies its coordinates, and the gid parameter identifies the glyph.
Auxillary steps for this strategy
- In horizontal mode, glyph baseline coordinates on the ‘Y’ axis remain constant; vertical mode advances them for each subsequent glyph.
- In horizontal mode, the ‘X’ coordinate advances by the previous glyph width and pair kerning, multiplied by the spacing coefficient.
- Both ‘X’ and ‘Y’ coordinates are passed into the TransformationMatrix object used by GlyphOutlineRenderer to draw glyphs.
- Horizontal advance is calculated for each subsequent glyph; vertical mode keeps the X coordinate fixed.
- Object of the GlyphOutlineRenderer type draws glyphs with the help of GlyphOutlinePainter not into Bitmap directly, but into the GraphicsPath object passed into the constructor for GlyphOutlinePainter, so we use the object of System.Drawing.Graphics type to draw GraphicsPath into Bitmap.
- The FontWidthToImageWidth() method converts glyph width from font units to pixels.
The first overload creates and disposes a bitmap. The second accepts a caller-owned bitmap and supports spacing, coordinates, overlays and vertical output. Add both overloads below to GlyphOutlinePainter:
1// Render text with spacing, coordinates, overlays and vertical layout
2
3public static void CustomDrawText(string text, IFont font, double fontSize,
4 Brush backgroundBrush, Brush textBrush, string outFile)
5{
6 using var bitmap = new Bitmap(960, 720);
7 CustomDrawText(text, font, fontSize, backgroundBrush, textBrush, outFile, bitmap);
8}
9
10public static void CustomDrawText(string text, IFont font, double fontSize,
11 Brush backgroundBrush, Brush textBrush, string outFile, Bitmap bitmap,
12 double kerningCoefficient = 1, double coordinateX = 0, double coordinateY = 0, bool topDown = false)
13{
14 const double dpi = 300;
15 double scale = fontSize * dpi / 72;
16 bitmap.SetResolution((float)dpi, (float)dpi);
17 using (Graphics graphics = Graphics.FromImage(bitmap))
18 {
19 // A transparent background preserves an existing image for overlays.
20 graphics.FillRectangle(backgroundBrush, 0, 0, bitmap.Width, bitmap.Height);
21 graphics.SmoothingMode = SmoothingMode.HighQuality;
22 GlyphId? previousGid = null;
23 double glyphXCoordinate = coordinateX;
24 double glyphYCoordinate = coordinateY + scale;
25
26 foreach (char character in text)
27 {
28 GlyphId gid = font.Encoding.DecodeToGid(character);
29 if (gid == null) continue;
30 Glyph glyph = font.GlyphAccessor.GetGlyphById(gid);
31 if (glyph == null) continue;
32
33 if (previousGid != null)
34 {
35 if (topDown)
36 glyphYCoordinate += scale;
37 else
38 {
39 double advance = FontWidthToImageWidth(font.Metrics.GetGlyphWidth(previousGid),
40 glyph.SourceResolution, fontSize, dpi);
41 double kerning = font.Metrics.GetKerningValue(previousGid, gid)
42 / (double)glyph.SourceResolution * scale;
43 // Like the source example, the coefficient scales the whole advance.
44 glyphXCoordinate += (advance + kerning) * kerningCoefficient;
45 }
46 }
47
48 using var path = new GraphicsPath();
49 var painter = new GlyphOutlinePainter(path);
50 var renderer = new GlyphOutlineRenderer(painter);
51 var matrix = new TransformationMatrix(new double[]
52 {
53 scale, 0, 0, -scale, glyphXCoordinate, glyphYCoordinate
54 });
55 renderer.RenderGlyph(font, gid, matrix);
56 path.FillMode = FillMode.Winding;
57 graphics.FillPath(textBrush, path);
58 previousGid = gid;
59 }
60 }
61 // The caller owns the supplied bitmap and may reuse it for another overlay.
62 bitmap.Save(outFile, ImageFormat.Png);
63}Add this utility method to the same class to convert font width to image width:
1// Convert font units to image pixels
2
3private static double FontWidthToImageWidth(double width, int fontSourceResolution,
4 double fontSize, double dpi = 300)
5{
6 return width / fontSourceResolution * fontSize * dpi / 72;
7}The next example renders “Hello world” using Verdana with black text on white and Lato with blue text on yellow. It saves Custom_Verdana.png and Custom_Lato.png:
1// Render two fonts with a custom glyph painter
2
3string verdanaPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4string latoPath = Path.Combine(DataDir, "TTF", "LatoWeb-Regular.ttf");
5Font verdana = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(verdanaPath)));
6Font lato = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(latoPath)));
7string verdanaOutput = Path.Combine(OutputDir, "Custom_Verdana.png");
8string latoOutput = Path.Combine(OutputDir, "Custom_Lato.png");
9GlyphOutlinePainter.CustomDrawText("Hello world", verdana, 24, Brushes.White, Brushes.Black, verdanaOutput);
10GlyphOutlinePainter.CustomDrawText("Hello world", lato, 24, Brushes.Yellow, Brushes.Blue, latoOutput);The implementation result:
Verdana
Lato
Kerning
The helper obtains pair kerning from the font. Setting kerningCoefficient to 1.25 increases the entire horizontal advance (the previous glyph width plus pair kerning) by 25%. This example uses Verdana and saves IncreasedSpacing.png:
1// Increase the horizontal glyph advance
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "IncreasedSpacing.png");
6using var bitmap = new Bitmap(960, 720);
7GlyphOutlinePainter.CustomDrawText("Hello world", font, 18, Brushes.White, Brushes.Black,
8 outFile, bitmap, kerningCoefficient: 1.25);The implementation result:
Verdana with a spacing coefficient of 1.25
Rendering text by coordinates
Use coordinateX and coordinateY to position the text. coordinateY is the offset above the first baseline; the helper adds fontSize * 300 / 72 pixels. At 18 points, coordinateX: 300 and coordinateY: 225 place the first baseline at (300, 300). The example saves PositionedText.png:
1// Position text at explicit coordinates
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "PositionedText.png");
6using var bitmap = new Bitmap(960, 720);
7// At 18 points and 300 DPI the 75-pixel baseline offset makes the baseline Y=300.
8GlyphOutlinePainter.CustomDrawText("Hello world", font, 18, Brushes.White, Brushes.Black,
9 outFile, bitmap, coordinateX: 300, coordinateY: 225);The implementation result:
Verdana, first baseline at X=300 and Y=300
How to add the text to an image
You can add text to an existing image by passing a bitmap and a transparent background brush to the shared helper. The transparent brush preserves the existing pixels.
The bitmap overload of CustomDrawText() shown above already supports this scenario. It draws onto the supplied bitmap and saves it as PNG; the caller retains ownership of the bitmap.
The example first creates OverlayBase.png in OutputDir, then opens that image to add a gray shadow and a brown annotation. It saves Shadow.png and ShadowAnnotated.png separately; no external input image is required:
1// Add a shadow and annotation to an existing image
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string inputImagePath = Path.Combine(OutputDir, "OverlayBase.png");
6GlyphOutlinePainter.CustomDrawText("Hello world", font, 17, Brushes.White, Brushes.Black, inputImagePath);
7using var bitmap = new Bitmap(inputImagePath);
8string shadowFile = Path.Combine(OutputDir, "Shadow.png");
9GlyphOutlinePainter.CustomDrawText("Hello world", font, 17, Brushes.Transparent, Brushes.Gray,
10 shadowFile, bitmap, coordinateX: -3);
11string annotatedFile = Path.Combine(OutputDir, "ShadowAnnotated.png");
12GlyphOutlinePainter.CustomDrawText("<= Shadow effect", font, 17, Brushes.Transparent, Brushes.Brown,
13 annotatedFile, bitmap, coordinateX: 400);The implementation result:
Verdana with a shadow
Verdana with a shadow and annotation. The annotation is clipped at the right edge of the 960-pixel canvas.
Outputting the text from top to bottom
The same bitmap overload supports vertical output through topDown: true. It places the first glyph on the initial baseline and advances subsequent glyphs by fontSize * 300 / 72 pixels.
The example renders “Top down” in Verdana at X=400 and saves TopDown.png. With coordinateY left at zero and an 18-point font, the first baseline is at Y=75 pixels:
1// Render text vertically from top to bottom
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "TopDown.png");
6using var bitmap = new Bitmap(960, 720);
7GlyphOutlinePainter.CustomDrawText("Top down", font, 18, Brushes.White, Brushes.Black,
8 outFile, bitmap, coordinateX: 400, topDown: true);The implementation result:
Verdana, top to bottom
Additional Information
The helper uses 300 DPI. The value scale = fontSize * dpi / 72 converts the font size in points to pixels. glyphXCoordinate and glyphYCoordinate specify the glyph baseline position. For horizontal text, the next position includes the previous glyph width and the pair kerning returned by the font; kerningCoefficient scales their sum.