3

My application allows users to create text objects on a canvas. This object can be saved to a project file to be loaded later.

In order that the objects look the same after loading on various platforms I have implemented the text object as a path. This also allows users to use downloaded fonts and then open the project file on a different device without that font - without the text changing appearance.

The path is created using TTextLayout.ConvertToPath and drawn using TCanvas.FillPath. This works fine for most fonts, but has an issue with some others.

The image below shows the result (top) with the Bahnschrift font. Bottom shows how it should look using MS Paint. This font seems to have intersecting paths and I think the issue is that FillPath is using an alternate fill mode, which doesn't seem to be an option to change.

I have also tested the same font in Inkscape as an SVG by creating the text and converting it to a path, but it's drawn correctly. The path data created by Delphi and Inkscape are essentially the same (the t consists of 2 closed regions that cross each other), so it's the way they're drawn that must be different.

Can anyone suggest a fix for this?

enter image description here

Here's the code

procedure TMainForm.Button1Click(Sender: TObject);
Var
  LPath : TPathData;
  LLayout : TTextLayout;
begin
  LLayout := TTextLayoutManager.DefaultTextLayout.Create;
  LLayout.Text := 'test';
  LLayout.Font.Family := 'Bahnschrift';

  LPath := TPathData.Create;
  LLayout.ConvertToPath(LPath);

  // Draw the path to a bitmap
  SavePathBitmap(LPath, 'Path_test');

  LLayout.Free;
  LPath.Free;
end;

procedure TMainForm.SavePathBitmap(APath : TPathData ; AFileName : String);
var
  bmp : TBitmap;
  rect : TRectF;
begin
  APath.Scale(10, 10); // Enlarge
  rect := APath.GetBounds;
  APath.Translate(-rect.Left + 5, -rect.Top + 5); // offset onto bitmap
  bmp := TBitmap.Create(Trunc(rect.Width)+10, Trunc(rect.Height)+10);
  with bmp.Canvas do if BeginScene then begin
    Clear(TAlphaColorRec.White);
    Fill.Color := TAlphaColorRec.Black;
    FillPath(APath, 1);
    EndScene;
    bmp.SaveToFile(AFileName + '.png');
  end;
  bmp.Free;
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
XylemFlow
  • 963
  • 5
  • 12
  • 1
    The Delphi version and code that demonstrates the issue would help others provide answers to your question. – Brian Sep 30 '20 at 13:02
  • The issue is related to FillMode when drawing geometries (A path is a geometry in Direct2D). Unfortunately, FMX canvas do not expose methods nor properties to change the FillMode. A solution which is not very simple is to use Windows API to create the bitmap with correct FillMode. For reference, see https://learn.microsoft.com/en-us/windows/win32/api/d2d1/ne-d2d1-d2d1_fill_mode – fpiette Sep 30 '20 at 14:41
  • I should add the Direct2D is not the only canvas used by FMX. Direct2D is Windows stuff only. The solution will be different on another platform! – fpiette Sep 30 '20 at 14:52
  • I'd like a multiplatform solution unfortunately. I wonder if it would be possible to separate the 2 path sections into separate paths? The other problem is that characters such as 'O' contain two closed paths also and the alternate FillMode is needed in order that the center is not filled. If I was able to change FillMode then would an 'O' be drawn with a filled center? Perhaps the winding order is reversed for the center of the 'O', in which case I could separate a path into 2 only if the winding orders are the same? Seems complex though. – XylemFlow Sep 30 '20 at 15:01
  • Even if I was able to fix the 't' by separating closed paths, it wouldn't solve the 'e', which is a single closed path. I may look for a solution using Windows API and something similar for OSX, but would this use the GPU as FMX does? – XylemFlow Sep 30 '20 at 15:12
  • For Windows API, yes it will use the GPU provided you use the Direct2D functions. – fpiette Sep 30 '20 at 16:05

0 Answers0