3

I would like to create a Direct2D path geometry from text. As I understood, I'll need to create an IDWriteFontFace, from which I'll have to call GetGlyphRunOutline.

Unfortunately, I cannot figure out how to create that font face. So far, I stumble on even creating a font file reference, which I think I have to use to create the font face.

procedure CreateFontFace;
var
  hr: HRESULT;
  FontDir: string;
  FontPath: string;
  ft: _FILETIME;
  FontFile: IDWriteFontFile;
  FontFace: IDWriteFontFace;
begin

  FontDir := GetSpecialFolder(CSIDL_FONTS);
  FontPath := IncludeTrailingPathDelimiter(FontDir) + 'Arial.ttf';

  // Here, FontPath contains 'C:\Windows\Fonts\Arial.ttf' 
  // (which exists on my machine)

  ft.dwLowDateTime := 0;
  ft.dwHighDateTime := 0;

  hr := DWriteFactory.CreateFontFileReference( 
   FontPath, // DOES NOT COMPILE
   ft,
   FontFile);

  if Succeeded(hr) then begin
    hr := DWriteFactory.CreateFontFace(
      DWRITE_FONT_FACE_TYPE_TRUETYPE,
      1,
      @FontFile,
      0,
      DWRITE_FONT_SIMULATIONS_NONE,
      FontFace);
  end;

end;

The prototype of CreateFontFileReference in Winapi.D2D1 is as follow:

    function CreateFontFileReference(var filePath: WCHAR;
      var lastWriteTime: FILETIME;
      out fontFile: IDWriteFontFile): HResult; stdcall;

I understand that putting a string instead of a WCHAR can bother the compiler, but how should this be written? I'm also interested if there is another, simpler way...

UPDATE: As stated by Remy Lebeau, there are other similar buggy declarations in the Winapi.D2D1 unit. The second one that I encountered is even in CreateFontFileReference too: parameter lastWriteTime should be a pointer, so to make my code work, I had to change my use of the ft variable as follows:

var
  ...
  ft: ^_FILETIME;
  ...
begin
  ...
  ft := nil;

  hr := DWriteFactory.CreateFontFileReference( 
    PChar(FontPath)^,
    ft^, // Yes, I am dereferencing nil, and it's working!
    FontFile);
  ...
end;
Papaya
  • 332
  • 3
  • 15

1 Answers1

3

If you are using Delphi 2009 or later, where String is Unicode, you need to typecast your String to PChar when passing it to CreateFontFileReference():

hr := DWriteFactory.CreateFontFileReference( 
  PChar(FontPath),
  ft,
  FontFile);

If you are using Delphi 2007 or earlier, where String is Ansi, you need to convert your String to a WideString first and then typecast that to PWideChar:

hr := DWriteFactory.CreateFontFileReference( 
  PWideChar(WideString(FontPath)),
  ft,
  FontFile);

Update: turns out there is a bug in the declaration of the first parameter of CreateFontFileReference(). Embarcadero declares it as var filePath: WCHAR, but it should have been declared as const filePath: PWCHAR instead. So you will have to account for that bug by dereferencing the PChar/PWideChar pointer, eg:

hr := DWriteFactory.CreateFontFileReference( 
  PChar(FontPath)^,
  ...);

hr := DWriteFactory.CreateFontFileReference( 
  PWideChar(WideString(FontPath))^,
  ...);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I am using Delphi XE2, but typecasting the string to a PChar makes a compiler error (E2033 Types of actual and formal var parameters must be identical). – Papaya Jun 28 '13 at 07:21
  • The declaration of the first parameter of `CreateFontFileReference()` in XE2 is wrong. It is declared as `var filePath: WCHAR`, but it should have been declared as `const filePath: PWCHAR` instead. That is a bug on Embarcadero's part. There are other sections of the `Winapi.D2D1` unit that are affected by the same bug. I updated my answer. – Remy Lebeau Jun 28 '13 at 20:09
  • Your updated answer works, thanks a lot for the dereferencing trick. – Papaya Jun 29 '13 at 18:19
  • You should file a bug report to QC, since this is a translation bug on Embarcadero's part. – Remy Lebeau Jun 29 '13 at 20:02
  • This is one of dozens of errors in `Winapi.D2D1.pas` – Warren P Sep 04 '14 at 16:47