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:
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:
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:
- 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 currently processed glyph using GlyphOutlineRenderer.RenderGlyph() Method. Aspose.Fonts.Matrix object is used to specify glyph coordinates. The glyph to render is specified by the gid parameter.
Auxillary steps for this strategy
- Glyph coordinates for ‘Y’ axis are constant for this code snippet.
- Glyph coordinates for ‘X’ axis are calculated for every processed glyph.
- Both ‘X’ and ‘Y’ coordinates are passed into Aspose.Fonts.Matrix object used by GlyphOutlineRenderer to draw glyphs.
- The distance between just and previously processed glyphs is calculated on every iteration step. It affects every glyph ‘X’ coordinate.
- 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 FontWidthToImageWith() Method calculates glyph width for the bitmap coordinate system.
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:
Arial
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:
Arial kernint
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:
Arial x=300 y=300
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:
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:
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;