Colorful Text with FMX

Small tip: Convert text to paths to jazz up your text rendering in FMX

The text rendering in FMX allows you to draw text by calling the TCanvas.FillText method.  However, the rendered text is very plain and doesn’t take advantage of gradient or bitmap fills and ignores the stroke properties entirely:

Boring Text Screenshot

Boring Text Screenshot

In the screenshot above, I painted inside a TPaintBox (top) by copying the font from a TText object (middle) and the Gradient Fill and Stroke properties from a TRectangle (bottom).  The TText fill text output is black (bottom) and the TPaintBox (top) outputs a dim, solid “Hello World!!!” text:

procedure TfrmFancyText.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
 Canvas.Font.Assign( Text1.TextSettings.Font );
 Canvas.Fill := Rectangle1.Fill;
 Canvas.Stroke.Assign( Rectangle1.Stroke );
 Canvas.FillText(PaintBox1.ClipRect, 'Hello World!!!', False, 1, [], TTextAlign.Center );
end;

Luckily, the FMX library allows us to jazz up our text by just doing a little work.  By using the TTextLayout class to convert text to paths, we can then do anything we want with the text path, including filling and stroking.

Colorful Text Screenshot

Colorful Text Screenshot. Notice that the top text has gradients for both the fill and outline of the text

The TTextLayout.ConvertToPath method converts any text to a path using the font object you specify.  To convert text to paths, you first need to create your TTextLayout and TPathData objects.  Then you need to assign the font you want for the text, the Text to convert, and finally call the ConvertToPath method.  After you have your path data, you can do anything you want with it.

procedure TfrmFancyText.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var
 TextLayout: TTextLayout;
 TextPath: TPathData;
begin
 Canvas.Fill := Rectangle1.Fill;
 Canvas.Stroke.Assign( Rectangle1.Stroke );

 TextLayout := TTextLayoutManager.DefaultTextLayout.Create;
 try
  TextPath := TPathData.Create;
  try
   TextLayout.Font := Text1.TextSettings.Font;
   TextLayout.Text := 'Hello World!!!';
   TextLayout.ConvertToPath(TextPath);
   Canvas.FillPath(TextPath, 1);
   Canvas.DrawPath(TextPath, 1);
  finally
   TextPath.Free;
  end;
 finally
  TextLayout.Free;
 end;
end;

There are a few problems with converting text to paths.  The first problem is that your path text cannot be placed anywhere you want without a little matrix math 🙂  If you notice in the second screenshot above, the gradient-filled Hello World is in the upper left.  This is because the created path is based off the 0, 0 point and the TCanvas draws a path whereever the path points are.  To move the path, you need to create a translation matrix and either apply it to the TPathData or temporarily set the TCanvas.Matrix property:

   [...]   
   TextLayout.ConvertToPath(TextPath);
   TextPath.ApplyMatrix(TMatrix.CreateTranslation(100,50));
   Canvas.FillPath(TextPath, 1);
   Canvas.DrawPath(TextPath, 1);
   [...]

The next problem, and perhaps the biggest one for some people, is that not all FMX Canvas objects support the ConvertToPath method.  The GPU Canvas (i.e., FMX.Types.GlobalUseGPUCanvas := True) implementation of the ConvertToPath method is blank!  (Update 2016/01/26: earlier versions of Delphi FMX GPU Canvases did not support ConvertToPath; it is supported since at least XE8)

Finally, the ConvertToPath method does not support all font styles, specifically fsUnderline and fsStrikeOut.  These two styles are ignored and the line will not be converted to a path.  But to me, all of these are minor annoyances, and it is just very cool to be able to convert text to paths without a lot of work.  It also, as you have seen, allows us to get around limitations in the text rendering in FMX.  I hope you find this small tip useful.

Happy CodeSmithing!

Leave a Reply

Your email address will not be published. Required fields are marked *